From 40e2cb4475f7bde9b50c6b9ece674374d134ed04 Mon Sep 17 00:00:00 2001 From: lady ada Date: Sun, 17 Jan 2021 13:01:03 -0500 Subject: [PATCH 1/9] reset and serial --- adafruit_sht4x.py | 155 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 154 insertions(+), 1 deletion(-) diff --git a/adafruit_sht4x.py b/adafruit_sht4x.py index a24581a..29faeba 100644 --- a/adafruit_sht4x.py +++ b/adafruit_sht4x.py @@ -26,7 +26,160 @@ """ -# imports +import time +import adafruit_bus_device.i2c_device as i2c_device +from micropython import const __version__ = "0.0.0-auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_SHT4x.git" + + +_SHT4X_DEFAULT_ADDR = const(0x44) # SHT4X I2C Address +_SHT4X_READSERIAL = const(0x89) # Read Out of Serial Register +_SHT4X_SOFTRESET = const(0x94) # Soft Reset + +class SHT4x: + """ + A driver for the SHT4x temperature and humidity sensor. + + :param ~busio.I2C i2c_bus: The `busio.I2C` object to use. This is the only required parameter. + + """ + + def __init__(self, i2c_bus): + self.i2c_device = i2c_device.I2CDevice(i2c_bus, _SHT4X_DEFAULT_ADDR) + self._buffer = bytearray(6) + self.reset() + + def _write_command(self, command): + self._buffer[0] = command >> 8 + self._buffer[1] = command & 0xFF + + with self.i2c_device as i2c: + i2c.write(self._buffer, start=0, end=2) + + @property + def serial_number(self): + self._buffer[0] = _SHT4X_READSERIAL + with self.i2c_device as i2c: + i2c.write(self._buffer, end=1) + time.sleep(0.01) + i2c.readinto(self._buffer) + + ser1 = self._buffer[0:2] + ser1_crc = self._buffer[2] + ser2 = self._buffer[3:5] + ser2_crc = self._buffer[5] + + # check CRC of bytes + if ser1_crc != self._crc8(ser1) or ser2_crc != self._crc8(ser2): + raise RuntimeError("Invalid CRC calculated") + + serial = (ser1[0] << 24) + (ser1[1] << 16) + (ser2[0] << 8) + ser2[1] + return serial + + def reset(self): + """Perform a soft reset of the sensor, resetting all settings to their power-on defaults""" + self._buffer[0] = _SHT4X_SOFTRESET + with self.i2c_device as i2c: + i2c.write(self._buffer, end=1) + + time.sleep(0.001) + + @property + def sleeping(self): + """Determines the sleep state of the sensor""" + return self._cached_sleep + + @sleeping.setter + def sleeping(self, sleep_enabled): + if sleep_enabled: + self._write_command(_SHTC3_SLEEP) + else: + self._write_command(_SHTC3_WAKEUP) + time.sleep(0.001) + self._cached_sleep = sleep_enabled + + # lowPowerMode(bool readmode) { _lpMode = readmode + + @property + def low_power(self): + """Enables the less accurate low power mode, trading accuracy for power consumption""" + return self._low_power + + @low_power.setter + def low_power(self, low_power_enabled): + self._low_power = low_power_enabled + + @property + def relative_humidity(self): + """The current relative humidity in % rH""" + return self.measurements[1] + + @property + def temperature(self): + """The current temperature in degrees celcius""" + return self.measurements[0] + + @property + def measurements(self): + """both `temperature` and `relative_humidity`, read simultaneously""" + + self.sleeping = False + temperature = None + humidity = None + # send correct command for the current power state + if self.low_power: + self._write_command(_SHTC3_LOWPOW_MEAS_TFIRST) + time.sleep(0.001) + else: + self._write_command(_SHTC3_NORMAL_MEAS_TFIRST) + time.sleep(0.013) + + # self._buffer = bytearray(6) + # read the measured data into our buffer + with self.i2c_device as i2c: + i2c.readinto(self._buffer) + + # separate the read data + temp_data = self._buffer[0:2] + temp_crc = self._buffer[2] + humidity_data = self._buffer[3:5] + humidity_crc = self._buffer[5] + + # check CRC of bytes + if temp_crc != self._crc8(temp_data) or humidity_crc != self._crc8( + humidity_data + ): + return (temperature, humidity) + + # decode data into human values: + # convert bytes into 16-bit signed integer + # convert the LSB value to a human value according to the datasheet + raw_temp = unpack_from(">H", temp_data)[0] + raw_temp = ((4375 * raw_temp) >> 14) - 4500 + temperature = raw_temp / 100.0 + + # repeat above steps for humidity data + raw_humidity = unpack_from(">H", humidity_data)[0] + raw_humidity = (625 * raw_humidity) >> 12 + humidity = raw_humidity / 100.0 + + self.sleeping = True + return (temperature, humidity) + + ## CRC-8 formula from page 14 of SHTC3 datasheet + # https://media.digikey.com/pdf/Data%20Sheets/Sensirion%20PDFs/HT_DS_SHTC3_D1.pdf + # Test data [0xBE, 0xEF] should yield 0x92 + + @staticmethod + def _crc8(buffer): + 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 # return the bottom 8 bits From 1125f1e44ba8a2630a4fc9ff8b360477be939e86 Mon Sep 17 00:00:00 2001 From: lady ada Date: Sun, 17 Jan 2021 13:08:21 -0500 Subject: [PATCH 2/9] read sensor --- adafruit_sht4x.py | 73 ++++++++++++++--------------------------------- 1 file changed, 22 insertions(+), 51 deletions(-) diff --git a/adafruit_sht4x.py b/adafruit_sht4x.py index 29faeba..a404255 100644 --- a/adafruit_sht4x.py +++ b/adafruit_sht4x.py @@ -27,6 +27,7 @@ """ import time +import struct import adafruit_bus_device.i2c_device as i2c_device from micropython import const @@ -38,6 +39,16 @@ _SHT4X_READSERIAL = const(0x89) # Read Out of Serial Register _SHT4X_SOFTRESET = const(0x94) # Soft Reset +_SHT4x_NOHEAT_HIGHPRECISION = const(0xFD) +_SHT4x_NOHEAT_MEDPRECISION = const(0xF6) +_SHT4x_NOHEAT_LOWPRECISION = const(0xE0) +_SHT4x_HIGHHEAT_1S = const(0x39) +_SHT4x_HIGHHEAT_100MS= const(0x32) +_SHT4x_MEDHEAT_1S = const(0x2F) +_SHT4x_MEDHEAT_100MS = const(0x24) +_SHT4x_LOWHEAT_1S = const(0x1E) +_SHT4x_LOWHEAT_100MS = const(0x15) + class SHT4x: """ A driver for the SHT4x temperature and humidity sensor. @@ -51,13 +62,6 @@ def __init__(self, i2c_bus): self._buffer = bytearray(6) self.reset() - def _write_command(self, command): - self._buffer[0] = command >> 8 - self._buffer[1] = command & 0xFF - - with self.i2c_device as i2c: - i2c.write(self._buffer, start=0, end=2) - @property def serial_number(self): self._buffer[0] = _SHT4X_READSERIAL @@ -86,31 +90,6 @@ def reset(self): time.sleep(0.001) - @property - def sleeping(self): - """Determines the sleep state of the sensor""" - return self._cached_sleep - - @sleeping.setter - def sleeping(self, sleep_enabled): - if sleep_enabled: - self._write_command(_SHTC3_SLEEP) - else: - self._write_command(_SHTC3_WAKEUP) - time.sleep(0.001) - self._cached_sleep = sleep_enabled - - # lowPowerMode(bool readmode) { _lpMode = readmode - - @property - def low_power(self): - """Enables the less accurate low power mode, trading accuracy for power consumption""" - return self._low_power - - @low_power.setter - def low_power(self, low_power_enabled): - self._low_power = low_power_enabled - @property def relative_humidity(self): """The current relative humidity in % rH""" @@ -125,20 +104,14 @@ def temperature(self): def measurements(self): """both `temperature` and `relative_humidity`, read simultaneously""" - self.sleeping = False temperature = None humidity = None - # send correct command for the current power state - if self.low_power: - self._write_command(_SHTC3_LOWPOW_MEAS_TFIRST) - time.sleep(0.001) - else: - self._write_command(_SHTC3_NORMAL_MEAS_TFIRST) - time.sleep(0.013) - - # self._buffer = bytearray(6) - # read the measured data into our buffer + command = _SHT4x_NOHEAT_HIGHPRECISION + with self.i2c_device as i2c: + self._buffer[0] = command + i2c.write(self._buffer, end=1) + time.sleep(0.1) i2c.readinto(self._buffer) # separate the read data @@ -151,21 +124,19 @@ def measurements(self): if temp_crc != self._crc8(temp_data) or humidity_crc != self._crc8( humidity_data ): - return (temperature, humidity) + raise RuntimeError("Invalid CRC calculated") # decode data into human values: # convert bytes into 16-bit signed integer # convert the LSB value to a human value according to the datasheet - raw_temp = unpack_from(">H", temp_data)[0] - raw_temp = ((4375 * raw_temp) >> 14) - 4500 - temperature = raw_temp / 100.0 + temperature = struct.unpack_from(">H", temp_data)[0] + temperature = -45.0 + 175.0 * temperature/65535.0 # repeat above steps for humidity data - raw_humidity = unpack_from(">H", humidity_data)[0] - raw_humidity = (625 * raw_humidity) >> 12 - humidity = raw_humidity / 100.0 + humidity = struct.unpack_from(">H", humidity_data)[0] + humidity = -6.0 + 125.0 * humidity/65535.0 + humidity = max(min(humidity, 100), 0) - self.sleeping = True return (temperature, humidity) ## CRC-8 formula from page 14 of SHTC3 datasheet From 7f5197966372a7d481c3452d2058133b0bba3959 Mon Sep 17 00:00:00 2001 From: lady ada Date: Sun, 17 Jan 2021 15:23:18 -0500 Subject: [PATCH 3/9] code and example! --- adafruit_sht4x.py | 68 +++++++++++++++++++++++++++++------- examples/sht4x_simpletest.py | 25 +++++++++++-- 2 files changed, 79 insertions(+), 14 deletions(-) diff --git a/adafruit_sht4x.py b/adafruit_sht4x.py index a404255..5f262c6 100644 --- a/adafruit_sht4x.py +++ b/adafruit_sht4x.py @@ -39,16 +39,47 @@ _SHT4X_READSERIAL = const(0x89) # Read Out of Serial Register _SHT4X_SOFTRESET = const(0x94) # Soft Reset -_SHT4x_NOHEAT_HIGHPRECISION = const(0xFD) -_SHT4x_NOHEAT_MEDPRECISION = const(0xF6) -_SHT4x_NOHEAT_LOWPRECISION = const(0xE0) -_SHT4x_HIGHHEAT_1S = const(0x39) -_SHT4x_HIGHHEAT_100MS= const(0x32) -_SHT4x_MEDHEAT_1S = const(0x2F) -_SHT4x_MEDHEAT_100MS = const(0x24) -_SHT4x_LOWHEAT_1S = const(0x1E) -_SHT4x_LOWHEAT_100MS = const(0x15) - + + +class CV: + """struct helper""" + @classmethod + def add_values(cls, value_tuples): + """Add CV values to the class""" + cls.string = {} + cls.delay = {} + + for value_tuple in value_tuples: + name, value, string, delay = value_tuple + setattr(cls, name, value) + cls.string[value] = string + cls.delay[value] = delay + + @classmethod + def is_valid(cls, value): + """Validate that a given value is a member""" + return value in cls.string + +class Mode(CV): + """Options for ``power_mode``""" + pass # pylint: disable=unnecessary-pass + + +Mode.add_values( + ( + ("NOHEAT_HIGHPRECISION", 0xFD, "No heater, high precision", 0.01), + ("NOHEAT_MEDPRECISION", 0xF6, "No heater, med precision", 0.005), + ("NOHEAT_LOWPRECISION", 0xE0, "No heater, low precision", 0.002), + ("HIGHHEAT_1S", 0x39, "High heat, 1 second", 1.1), + ("HIGHHEAT_100MS", 0x32, "High heat, 0.1 second", 0.11), + ("MEDHEAT_1S", 0x2F, "Med heat, 1 second", 1.1), + ("MEDHEAT_100MS", 0x24, "Med heat, 0.1 second", 0.11), + ("LOWHEAT_1S", 0x1E, "Low heat, 1 second", 1.1), + ("LOWHEAT_100MS", 0x15, "Low heat, 0.1 second", 0.11), + ) +) + + class SHT4x: """ A driver for the SHT4x temperature and humidity sensor. @@ -61,6 +92,7 @@ def __init__(self, i2c_bus): self.i2c_device = i2c_device.I2CDevice(i2c_bus, _SHT4X_DEFAULT_ADDR) self._buffer = bytearray(6) self.reset() + self._mode = Mode.NOHEAT_HIGHPRECISION @property def serial_number(self): @@ -90,6 +122,18 @@ def reset(self): time.sleep(0.001) + @property + def mode(self): + """Returns current sensor reading mode (heater and precision)""" + return self._mode + + @mode.setter + def mode(self, new_mode): + print(new_mode) + if not Mode.is_valid(new_mode): + raise AttributeError("mode must be a Mode") + self._mode = new_mode + @property def relative_humidity(self): """The current relative humidity in % rH""" @@ -106,12 +150,12 @@ def measurements(self): temperature = None humidity = None - command = _SHT4x_NOHEAT_HIGHPRECISION + command = self._mode with self.i2c_device as i2c: self._buffer[0] = command i2c.write(self._buffer, end=1) - time.sleep(0.1) + time.sleep(Mode.delay[self._mode]) i2c.readinto(self._buffer) # separate the read data diff --git a/examples/sht4x_simpletest.py b/examples/sht4x_simpletest.py index 0d4a377..3ae2572 100644 --- a/examples/sht4x_simpletest.py +++ b/examples/sht4x_simpletest.py @@ -1,3 +1,24 @@ -# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries +# SPDX-FileCopyrightText: Copyright (c) 2020 ladyada for Adafruit Industries # -# SPDX-License-Identifier: Unlicense +# SPDX-License-Identifier: MIT + +import time +import busio +import board +import adafruit_sht4x + +i2c = busio.I2C(board.SCL, board.SDA) +sht = adafruit_sht4x.SHT4x(i2c) +print("Found SHT4x with serial number", hex(sht.serial_number)) + +sht.mode = adafruit_sht4x.Mode.NOHEAT_HIGHPRECISION +# Can also set the mode to enable heater +# sht.mode = adafruit_sht4x.Mode.LOWHEAT_100MS +print("Current mode is: ", adafruit_sht4x.Mode.string[sht.mode]) + +while True: + temperature, relative_humidity = sht.measurements + print("Temperature: %0.1f C" % temperature) + print("Humidity: %0.1f %%" % relative_humidity) + print("") + time.sleep(1) From 2a18bf5a353ec64ce51e6f227b6b5eced60133e9 Mon Sep 17 00:00:00 2001 From: lady ada Date: Sun, 17 Jan 2021 15:26:26 -0500 Subject: [PATCH 4/9] linty --- adafruit_sht4x.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/adafruit_sht4x.py b/adafruit_sht4x.py index 5f262c6..e2d7369 100644 --- a/adafruit_sht4x.py +++ b/adafruit_sht4x.py @@ -79,7 +79,7 @@ class Mode(CV): ) ) - + class SHT4x: """ A driver for the SHT4x temperature and humidity sensor. @@ -92,10 +92,12 @@ def __init__(self, i2c_bus): self.i2c_device = i2c_device.I2CDevice(i2c_bus, _SHT4X_DEFAULT_ADDR) self._buffer = bytearray(6) self.reset() - self._mode = Mode.NOHEAT_HIGHPRECISION + self._mode = Mode.NOHEAT_HIGHPRECISION # pylint: disable=no-member + @property def serial_number(self): + """The unique 32-bit serial number""" self._buffer[0] = _SHT4X_READSERIAL with self.i2c_device as i2c: i2c.write(self._buffer, end=1) @@ -119,12 +121,11 @@ def reset(self): self._buffer[0] = _SHT4X_SOFTRESET with self.i2c_device as i2c: i2c.write(self._buffer, end=1) - time.sleep(0.001) @property def mode(self): - """Returns current sensor reading mode (heater and precision)""" + """The current sensor reading mode (heater and precision)""" return self._mode @mode.setter @@ -141,7 +142,7 @@ def relative_humidity(self): @property def temperature(self): - """The current temperature in degrees celcius""" + """The current temperature in degrees celsius""" return self.measurements[0] @property @@ -151,7 +152,7 @@ def measurements(self): temperature = None humidity = None command = self._mode - + with self.i2c_device as i2c: self._buffer[0] = command i2c.write(self._buffer, end=1) From 6265f4f050843380955f9f3c0623d52e7c0d19cd Mon Sep 17 00:00:00 2001 From: lady ada Date: Sun, 17 Jan 2021 15:28:26 -0500 Subject: [PATCH 5/9] add example to readme --- README.rst | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 7511a0f..9999433 100644 --- a/README.rst +++ b/README.rst @@ -64,7 +64,29 @@ To install in a virtual environment in your current project: Usage Example ============= -.. todo:: Add a quick, simple example. It and other examples should live in the examples folder and be included in docs/examples.rst. +.. code-block:: python + + import time + import busio + import board + import adafruit_sht4x + + i2c = busio.I2C(board.SCL, board.SDA) + sht = adafruit_sht4x.SHT4x(i2c) + print("Found SHT4x with serial number", hex(sht.serial_number)) + + sht.mode = adafruit_sht4x.Mode.NOHEAT_HIGHPRECISION + # Can also set the mode to enable heater + # sht.mode = adafruit_sht4x.Mode.LOWHEAT_100MS + print("Current mode is: ", adafruit_sht4x.Mode.string[sht.mode]) + + while True: + temperature, relative_humidity = sht.measurements + print("Temperature: %0.1f C" % temperature) + print("Humidity: %0.1f %%" % relative_humidity) + print("") + time.sleep(1) + Contributing ============ From 18110696f97564dc907ee03c9a6bbdb493177dae Mon Sep 17 00:00:00 2001 From: lady ada Date: Sun, 17 Jan 2021 15:29:24 -0500 Subject: [PATCH 6/9] blacken --- adafruit_sht4x.py | 15 ++++++++------- docs/conf.py | 7 +++++-- setup.py | 2 +- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/adafruit_sht4x.py b/adafruit_sht4x.py index e2d7369..8b178c5 100644 --- a/adafruit_sht4x.py +++ b/adafruit_sht4x.py @@ -36,13 +36,13 @@ _SHT4X_DEFAULT_ADDR = const(0x44) # SHT4X I2C Address -_SHT4X_READSERIAL = const(0x89) # Read Out of Serial Register +_SHT4X_READSERIAL = const(0x89) # Read Out of Serial Register _SHT4X_SOFTRESET = const(0x94) # Soft Reset - class CV: """struct helper""" + @classmethod def add_values(cls, value_tuples): """Add CV values to the class""" @@ -60,8 +60,10 @@ def is_valid(cls, value): """Validate that a given value is a member""" return value in cls.string + class Mode(CV): """Options for ``power_mode``""" + pass # pylint: disable=unnecessary-pass @@ -76,7 +78,7 @@ class Mode(CV): ("MEDHEAT_100MS", 0x24, "Med heat, 0.1 second", 0.11), ("LOWHEAT_1S", 0x1E, "Low heat, 1 second", 1.1), ("LOWHEAT_100MS", 0x15, "Low heat, 0.1 second", 0.11), - ) + ) ) @@ -92,8 +94,7 @@ def __init__(self, i2c_bus): self.i2c_device = i2c_device.I2CDevice(i2c_bus, _SHT4X_DEFAULT_ADDR) self._buffer = bytearray(6) self.reset() - self._mode = Mode.NOHEAT_HIGHPRECISION # pylint: disable=no-member - + self._mode = Mode.NOHEAT_HIGHPRECISION # pylint: disable=no-member @property def serial_number(self): @@ -175,11 +176,11 @@ def measurements(self): # convert bytes into 16-bit signed integer # convert the LSB value to a human value according to the datasheet temperature = struct.unpack_from(">H", temp_data)[0] - temperature = -45.0 + 175.0 * temperature/65535.0 + temperature = -45.0 + 175.0 * temperature / 65535.0 # repeat above steps for humidity data humidity = struct.unpack_from(">H", humidity_data)[0] - humidity = -6.0 + 125.0 * humidity/65535.0 + humidity = -6.0 + 125.0 * humidity / 65535.0 humidity = max(min(humidity, 100), 0) return (temperature, humidity) diff --git a/docs/conf.py b/docs/conf.py index 74786ce..3c650e1 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -29,8 +29,11 @@ intersphinx_mapping = { - "python": ("https://docs.python.org/3.4", None),"BusDevice": ("https://circuitpython.readthedocs.io/projects/busdevice/en/latest/", None), - + "python": ("https://docs.python.org/3.4", None), + "BusDevice": ( + "https://circuitpython.readthedocs.io/projects/busdevice/en/latest/", + None, + ), "CircuitPython": ("https://circuitpython.readthedocs.io/en/latest/", None), } diff --git a/setup.py b/setup.py index 3881f8c..69ef60a 100644 --- a/setup.py +++ b/setup.py @@ -53,7 +53,7 @@ ], # What does your project relate to? keywords="adafruit blinka circuitpython micropython sht4x temperature humidity sensor " - "sht40 sensirion", + "sht40 sensirion", # You can just specify the packages manually here if your project is # simple. Or you can use find_packages(). # TODO: IF LIBRARY FILES ARE A PACKAGE FOLDER, From f91ad24259b67b8ae14d44b857de817916ab7460 Mon Sep 17 00:00:00 2001 From: lady ada Date: Sun, 17 Jan 2021 15:35:09 -0500 Subject: [PATCH 7/9] will pypi --- README.rst | 5 ----- 1 file changed, 5 deletions(-) diff --git a/README.rst b/README.rst index 9999433..96f9c67 100644 --- a/README.rst +++ b/README.rst @@ -33,11 +33,6 @@ This is easily achieved by downloading Installing from PyPI ===================== -.. note:: This library is not available on PyPI yet. Install documentation is included - as a standard element. Stay tuned for PyPI availability! - -.. todo:: Remove the above note if PyPI version is/will be available at time of release. - If the library is not planned for PyPI, remove the entire 'Installing from PyPI' section. On supported GNU/Linux systems like the Raspberry Pi, you can install the driver locally `from PyPI `_. To install for current user: From 51c80330c0faa8cf8ca586114d2782c84b6bbadf Mon Sep 17 00:00:00 2001 From: lady ada Date: Sun, 17 Jan 2021 15:38:44 -0500 Subject: [PATCH 8/9] remove todo --- docs/index.rst | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index 9237618..d82f91a 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -23,9 +23,6 @@ Table of Contents .. toctree:: :caption: Tutorials -.. todo:: Add any Learn guide links here. If there are none, then simply delete this todo and leave - the toctree above for use later. - .. toctree:: :caption: Related Products From fd8b7e6962928dffbd0ddad62242d754e59bdd4a Mon Sep 17 00:00:00 2001 From: lady ada Date: Sun, 17 Jan 2021 15:49:23 -0500 Subject: [PATCH 9/9] linky --- docs/index.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index d82f91a..6455512 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -26,8 +26,7 @@ Table of Contents .. toctree:: :caption: Related Products -.. todo:: Add any product links here. If there are none, then simply delete this todo and leave - the toctree above for use later. + Purchase SHT40 breakout .. toctree:: :caption: Other Links