PiClock project

This commit is contained in:
2024-01-03 09:59:59 +01:00
parent edc186dbd4
commit ca21954980
9 changed files with 1737 additions and 0 deletions

261
sht4x/sht4x.py Normal file
View 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)