New sensors: SHT4x, BMP390
This commit is contained in:
261
sht4x/sht4x.py
Normal file
261
sht4x/sht4x.py
Normal file
@@ -0,0 +1,261 @@
|
||||
# SPDX-FileCopyrightText: Copyright (c) 2023 Jose D. Montoya
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
"""
|
||||
`sht4x`
|
||||
================================================================================
|
||||
|
||||
MicroPython Driver fot the Sensirion Temperature and Humidity SHT40, SHT41 and SHT45 Sensor
|
||||
|
||||
|
||||
* Author: Jose D. Montoya
|
||||
|
||||
|
||||
"""
|
||||
|
||||
import time
|
||||
import struct
|
||||
from micropython import const
|
||||
|
||||
try:
|
||||
from typing import Tuple
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
__version__ = "0.0.0+auto.0"
|
||||
__repo__ = "https://github.com/jposada202020/MicroPython_SHT4X.git"
|
||||
|
||||
_RESET = const(0x94)
|
||||
|
||||
HIGH_PRECISION = const(0)
|
||||
MEDIUM_PRECISION = const(1)
|
||||
LOW_PRECISION = const(2)
|
||||
temperature_precision_options = (HIGH_PRECISION, MEDIUM_PRECISION, LOW_PRECISION)
|
||||
temperature_precision_values = {
|
||||
HIGH_PRECISION: const(0xFD),
|
||||
MEDIUM_PRECISION: const(0xF6),
|
||||
LOW_PRECISION: const(0xE0),
|
||||
}
|
||||
|
||||
HEATER200mW = const(0)
|
||||
HEATER110mW = const(1)
|
||||
HEATER20mW = const(2)
|
||||
heater_power_values = (HEATER200mW, HEATER110mW, HEATER20mW)
|
||||
|
||||
TEMP_1 = const(0)
|
||||
TEMP_0_1 = const(1)
|
||||
heat_time_values = (TEMP_1, TEMP_0_1)
|
||||
|
||||
wat_config = {
|
||||
HEATER200mW: (0x39, 0x32),
|
||||
HEATER110mW: (0x2F, 0x24),
|
||||
HEATER20mW: (0x1E, 0x15),
|
||||
}
|
||||
|
||||
|
||||
class SHT4X:
|
||||
"""Driver for the SHT4X Sensor connected over I2C.
|
||||
|
||||
:param ~machine.I2C i2c: The I2C bus the SHT4X is connected to.
|
||||
:param int address: The I2C device address. Defaults to :const:`0x44`
|
||||
|
||||
:raises RuntimeError: if the sensor is not found
|
||||
|
||||
**Quickstart: Importing and using the device**
|
||||
|
||||
Here is an example of using the :class:`SHT4X` class.
|
||||
First you will need to import the libraries to use the sensor
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from machine import Pin, I2C
|
||||
from micropython_sht4x import sht4x
|
||||
|
||||
Once this is done you can define your `machine.I2C` object and define your sensor object
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
i2c = I2C(1, sda=Pin(2), scl=Pin(3))
|
||||
sht = sht4x.SHT4X(i2c)
|
||||
|
||||
Now you have access to the attributes
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
temp = sht.temperature
|
||||
hum = sht.relative_humidity
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, i2c, address: int = 0x44) -> None:
|
||||
self._i2c = i2c
|
||||
self._address = address
|
||||
self._data = bytearray(6)
|
||||
|
||||
self._command = 0xFD
|
||||
self._temperature_precision = HIGH_PRECISION
|
||||
self._heater_power = HEATER20mW
|
||||
self._heat_time = TEMP_0_1
|
||||
|
||||
@property
|
||||
def temperature_precision(self) -> str:
|
||||
"""
|
||||
Sensor temperature_precision
|
||||
|
||||
+------------------------------------+------------------+
|
||||
| Mode | Value |
|
||||
+====================================+==================+
|
||||
| :py:const:`sht4x.HIGH_PRECISION` | :py:const:`0` |
|
||||
+------------------------------------+------------------+
|
||||
| :py:const:`sht4x.MEDIUM_PRECISION` | :py:const:`1` |
|
||||
+------------------------------------+------------------+
|
||||
| :py:const:`sht4x.LOW_PRECISION` | :py:const:`2` |
|
||||
+------------------------------------+------------------+
|
||||
|
||||
"""
|
||||
values = ("HIGH_PRECISION", "MEDIUM_PRECISION", "LOW_PRECISION")
|
||||
return values[self._temperature_precision]
|
||||
|
||||
@temperature_precision.setter
|
||||
def temperature_precision(self, value: int) -> None:
|
||||
if value not in temperature_precision_values:
|
||||
raise ValueError("Value must be a valid temperature_precision setting")
|
||||
self._temperature_precision = value
|
||||
self._command = temperature_precision_values[value]
|
||||
|
||||
@property
|
||||
def relative_humidity(self) -> float:
|
||||
"""
|
||||
The current relative humidity in % rH
|
||||
The RH conversion formula (1) allows values to be reported
|
||||
which are outside the range of 0 %RH … 100 %RH. Relative
|
||||
humidity values which are smaller than 0 %RH and larger than
|
||||
100 %RH are non-physical, however these “uncropped” values might
|
||||
be found beneficial in some cases (e.g. when the distribution of
|
||||
the sensors at the measurement boundaries are of interest)
|
||||
"""
|
||||
return self.measurements[1]
|
||||
|
||||
@property
|
||||
def temperature(self) -> float:
|
||||
"""The current temperature in Celsius"""
|
||||
return self.measurements[0]
|
||||
|
||||
@property
|
||||
def measurements(self) -> Tuple[float, float]:
|
||||
"""both `temperature` and `relative_humidity`, read simultaneously
|
||||
If you use t the heater function, sensor will be not give a response
|
||||
back. Waiting time is added to the logic to account for this situation
|
||||
"""
|
||||
|
||||
self._i2c.writeto(self._address, bytes([self._command]), False)
|
||||
if self._command in (0x39, 0x2F, 0x1E):
|
||||
time.sleep(1.2)
|
||||
elif self._command in (0x32, 0x24, 0x15):
|
||||
time.sleep(0.2)
|
||||
time.sleep(0.2)
|
||||
self._i2c.readfrom_into(self._address, self._data)
|
||||
|
||||
temperature, temp_crc, humidity, humidity_crc = struct.unpack_from(
|
||||
">HBHB", self._data
|
||||
)
|
||||
|
||||
if temp_crc != self._crc(
|
||||
memoryview(self._data[0:2])
|
||||
) or humidity_crc != self._crc(memoryview(self._data[3:5])):
|
||||
raise RuntimeError("Invalid CRC calculated")
|
||||
|
||||
temperature = -45.0 + 175.0 * temperature / 65535.0
|
||||
|
||||
humidity = -6.0 + 125.0 * humidity / 65535.0
|
||||
humidity = max(min(humidity, 100), 0)
|
||||
|
||||
return temperature, humidity
|
||||
|
||||
@staticmethod
|
||||
def _crc(buffer) -> int:
|
||||
"""verify the crc8 checksum"""
|
||||
crc = 0xFF
|
||||
for byte in buffer:
|
||||
crc ^= byte
|
||||
for _ in range(8):
|
||||
if crc & 0x80:
|
||||
crc = (crc << 1) ^ 0x31
|
||||
else:
|
||||
crc = crc << 1
|
||||
return crc & 0xFF
|
||||
|
||||
@property
|
||||
def heater_power(self) -> str:
|
||||
"""
|
||||
Sensor heater power
|
||||
The sensor has a heater. Three heating powers and two heating
|
||||
durations are selectable.
|
||||
The sensor executes the following procedure:
|
||||
1. The heater is enabled, and the timer starts its count-down.
|
||||
2. Measure is taken after time is up
|
||||
3. After the measurement is finished the heater is turned off.
|
||||
4. Temperature and humidity values are now available for readout.
|
||||
The maximum on-time of the heater commands is one second in order
|
||||
to prevent overheating
|
||||
|
||||
+-------------------------------+---------------+
|
||||
| Mode | Value |
|
||||
+===============================+===============+
|
||||
| :py:const:`sht4x.HEATER200mW` | :py:const:`0` |
|
||||
+-------------------------------+---------------+
|
||||
| :py:const:`sht4x.HEATER110mW` | :py:const:`1` |
|
||||
+-------------------------------+---------------+
|
||||
| :py:const:`sht4x.HEATER20mW` | :py:const:`2` |
|
||||
+-------------------------------+---------------+
|
||||
|
||||
"""
|
||||
values = ("HEATER200mW", "HEATER110mW", "HEATER20mW")
|
||||
return values[self._heater_power]
|
||||
|
||||
@heater_power.setter
|
||||
def heater_power(self, value: int) -> None:
|
||||
if value not in heater_power_values:
|
||||
raise ValueError("Value must be a valid heater power setting")
|
||||
self._heater_power = value
|
||||
self._command = wat_config[value][self._heat_time]
|
||||
|
||||
@property
|
||||
def heat_time(self) -> str:
|
||||
"""
|
||||
Sensor heat_time
|
||||
The sensor has a heater. Three heating powers and two heating
|
||||
durations are selectable.
|
||||
The sensor executes the following procedure:
|
||||
1. The heater is enabled, and the timer starts its count-down.
|
||||
2. Measure is taken after time is up
|
||||
3. After the measurement is finished the heater is turned off.
|
||||
4. Temperature and humidity values are now available for readout.
|
||||
The maximum on-time of the heater commands is one second in order
|
||||
to prevent overheating
|
||||
|
||||
+----------------------------+---------------+
|
||||
| Mode | Value |
|
||||
+============================+===============+
|
||||
| :py:const:`sht4x.TEMP_1` | :py:const:`0` |
|
||||
+----------------------------+---------------+
|
||||
| :py:const:`sht4x.TEMP_0_1` | :py:const:`1` |
|
||||
+----------------------------+---------------+
|
||||
"""
|
||||
values = ("TEMP_1", "TEMP_0_1")
|
||||
return values[self._heat_time]
|
||||
|
||||
@heat_time.setter
|
||||
def heat_time(self, value: int) -> None:
|
||||
if value not in heat_time_values:
|
||||
raise ValueError("Value must be a valid heat_time setting")
|
||||
self._heat_time = value
|
||||
self._command = wat_config[self._heater_power][value]
|
||||
|
||||
def reset(self):
|
||||
"""
|
||||
Reset the sensor
|
||||
"""
|
||||
self._i2c.writeto(self._address, bytes([_RESET]), False)
|
||||
time.sleep(0.1)
|
||||
Reference in New Issue
Block a user