From ec4841d68b8d66bbdcdf11bff55211f4895252cb Mon Sep 17 00:00:00 2001 From: Bruce Duncan Date: Fri, 7 Jun 2019 19:25:46 +0100 Subject: [PATCH 1/5] Add dew point measurement. Add a simple method to provide temperature and dew point measurements. --- sht30.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/sht30.py b/sht30.py index 8483a20..ca059b4 100644 --- a/sht30.py +++ b/sht30.py @@ -163,6 +163,19 @@ def measure_int(self, raw=False): h_dec = (aux % 0xffff * 100) // 0xffff return t_int, t_dec, h_int, h_dec + def measure_dp(self): + """ + Get the temperature (T) and approximate dew point (Tdp) measurements in + degrees Celsius. Dew point is calculated using a simple approximation + valid with RH > 0.5. + + References: + * https://en.wikipedia.org/wiki/Dew_point#Calculating_the_dew_point + """ + + temp, humidity = self.measure() + return temp, temp - (100 - humidity) / 5 + class SHT30Error(Exception): """ From 183561bc10779ecaf815cad4015b130d005623d5 Mon Sep 17 00:00:00 2001 From: Bruce Duncan Date: Fri, 7 Jun 2019 19:30:33 +0100 Subject: [PATCH 2/5] Add alternate I2C address constant. --- sht30.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sht30.py b/sht30.py index ca059b4..8b65fd8 100644 --- a/sht30.py +++ b/sht30.py @@ -7,6 +7,8 @@ # I2C address B 0x45 ADDR (pin 2) connected to VDD DEFAULT_I2C_ADDRESS = 0x45 +# I2C address A 0x44 ADDR (pin 2) connected to VSS +ALTERNATE_I2C_ADDRESS = 0x44 class SHT30(): """ From e7bd4d31ed81c932ae322eeb5ad99929fba4da7a Mon Sep 17 00:00:00 2001 From: Bruce Duncan Date: Fri, 7 Jun 2019 19:38:55 +0100 Subject: [PATCH 3/5] Tidy up spelling, whitespace, semicolons. General tidy up using pep8, fix some minor spelling and remove unnecessary semicolons. --- sht30.py | 86 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/sht30.py b/sht30.py index 8b65fd8..2471a24 100644 --- a/sht30.py +++ b/sht30.py @@ -10,11 +10,12 @@ # I2C address A 0x44 ADDR (pin 2) connected to VSS ALTERNATE_I2C_ADDRESS = 0x44 + class SHT30(): """ SHT30 sensor driver in pure python based on I2C bus - - References: + + References: * https://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/2_Humidity_Sensors/Sensirion_Humidity_Sensors_SHT3x_Datasheet_digital.pdf * https://www.wemos.cc/sites/default/files/2016-11/SHT30-DIS_datasheet.pdf * https://github.com/wemos/WEMOS_SHT3x_Arduino_Library @@ -22,13 +23,13 @@ class SHT30(): """ POLYNOMIAL = 0x131 # P(x) = x^8 + x^5 + x^4 + 1 = 100110001 - ALERT_PENDING_MASK = 0x8000 # 15 - HEATER_MASK = 0x2000 # 13 - RH_ALERT_MASK = 0x0800 # 11 - T_ALERT_MASK = 0x0400 # 10 - RESET_MASK = 0x0010 # 4 - CMD_STATUS_MASK = 0x0002 # 1 - WRITE_STATUS_MASK = 0x0001 # 0 + ALERT_PENDING_MASK = 0x8000 # 15 + HEATER_MASK = 0x2000 # 13 + RH_ALERT_MASK = 0x0800 # 11 + T_ALERT_MASK = 0x0400 # 10 + RESET_MASK = 0x0010 # 4 + CMD_STATUS_MASK = 0x0002 # 1 + WRITE_STATUS_MASK = 0x0001 # 0 # MSB = 0x2C LSB = 0x06 Repeatability = High, Clock stretching = enabled MEASURE_CMD = b'\x2C\x10' @@ -38,60 +39,60 @@ class SHT30(): ENABLE_HEATER_CMD = b'\x30\x6D' DISABLE_HEATER_CMD = b'\x30\x66' - def __init__(self, scl_pin=5, sda_pin=4, delta_temp = 0, delta_hum = 0, i2c_address=DEFAULT_I2C_ADDRESS): + def __init__(self, scl_pin=5, sda_pin=4, delta_temp=0, delta_hum=0, i2c_address=DEFAULT_I2C_ADDRESS): self.i2c = I2C(scl=Pin(scl_pin), sda=Pin(sda_pin)) self.i2c_addr = i2c_address self.set_delta(delta_temp, delta_hum) time.sleep_ms(50) - + def init(self, scl_pin=5, sda_pin=4): """ Init the I2C bus using the new pin values """ self.i2c.init(scl=Pin(scl_pin), sda=Pin(sda_pin)) - + def is_present(self): """ - Return true if the sensor is correctly conneced, False otherwise + Return true if the sensor is correctly connected, False otherwise """ return self.i2c_addr in self.i2c.scan() - - def set_delta(self, delta_temp = 0, delta_hum = 0): + + def set_delta(self, delta_temp=0, delta_hum=0): """ Apply a delta value on the future measurements of temperature and/or humidity The units are Celsius for temperature and percent for humidity (can be negative values) """ self.delta_temp = delta_temp self.delta_hum = delta_hum - + def _check_crc(self, data): # calculates 8-Bit checksum with given polynomial crc = 0xFF - + for b in data[:-1]: - crc ^= b; + crc ^= b for _ in range(8, 0, -1): if crc & 0x80: - crc = (crc << 1) ^ SHT30.POLYNOMIAL; + crc = (crc << 1) ^ SHT30.POLYNOMIAL else: crc <<= 1 crc_to_check = data[-1] return crc_to_check == crc - + def send_cmd(self, cmd_request, response_size=6, read_delay_ms=100): """ Send a command to the sensor and read (optionally) the response - The responsed data is validated by CRC + The response data is validated by CRC """ try: - self.i2c.start(); - self.i2c.writeto(self.i2c_addr, cmd_request); + self.i2c.start() + self.i2c.writeto(self.i2c_addr, cmd_request) if not response_size: - self.i2c.stop(); + self.i2c.stop() return time.sleep_ms(read_delay_ms) - data = self.i2c.readfrom(self.i2c_addr, response_size) - self.i2c.stop(); + data = self.i2c.readfrom(self.i2c_addr, response_size) + self.i2c.stop() for i in range(response_size//3): if not self._check_crc(data[i*3:(i+1)*3]): # pos 2 and 5 are CRC raise SHT30Error(SHT30Error.CRC_ERROR) @@ -107,58 +108,58 @@ def clear_status(self): """ Clear the status register """ - return self.send_cmd(SHT30.CLEAR_STATUS_CMD, None); + return self.send_cmd(SHT30.CLEAR_STATUS_CMD, None) def reset(self): """ Send a soft-reset to the sensor """ - return self.send_cmd(SHT30.RESET_CMD, None); + return self.send_cmd(SHT30.RESET_CMD, None) def status(self, raw=False): """ - Get the sensor status register. + Get the sensor status register. It returns a int value or the bytearray(3) if raw==True """ - data = self.send_cmd(SHT30.STATUS_CMD, 3, read_delay_ms=20); + data = self.send_cmd(SHT30.STATUS_CMD, 3, read_delay_ms=20) if raw: return data status_register = data[0] << 8 | data[1] return status_register - + def measure(self, raw=False): """ - If raw==True returns a bytearrya(6) with sensor direct measurement otherwise + If raw==True returns a bytearray(6) with sensor direct measurement otherwise It gets the temperature (T) and humidity (RH) measurement and return them. - + The units are Celsius and percent """ - data = self.send_cmd(SHT30.MEASURE_CMD, 6); + data = self.send_cmd(SHT30.MEASURE_CMD, 6) if raw: return data - t_celsius = (((data[0] << 8 | data[1]) * 175) / 0xFFFF) - 45 + self.delta_temp; - rh = (((data[3] << 8 | data[4]) * 100.0) / 0xFFFF) + self.delta_hum; + t_celsius = (((data[0] << 8 | data[1]) * 175) / 0xFFFF) - 45 + self.delta_temp + rh = (((data[3] << 8 | data[4]) * 100.0) / 0xFFFF) + self.delta_hum return t_celsius, rh def measure_int(self, raw=False): """ Get the temperature (T) and humidity (RH) measurement using integers. - If raw==True returns a bytearrya(6) with sensor direct measurement otherwise + If raw==True returns a bytearray(6) with sensor direct measurement otherwise It returns a tuple with 4 values: T integer, T decimal, H integer, H decimal For instance to return T=24.0512 and RH= 34.662 This method will return (24, 5, 34, 66) Only 2 decimal digits are returned, .05 becomes 5 Delta values are not applied in this method The units are Celsius and percent. """ - data = self.send_cmd(SHT30.MEASURE_CMD, 6); - if raw: + data = self.send_cmd(SHT30.MEASURE_CMD, 6) + if raw: return data aux = (data[0] << 8 | data[1]) * 175 - t_int = (aux // 0xffff) - 45; + t_int = (aux // 0xffff) - 45 t_dec = (aux % 0xffff * 100) // 0xffff aux = (data[3] << 8 | data[4]) * 100 h_int = aux // 0xffff @@ -183,14 +184,14 @@ class SHT30Error(Exception): """ Custom exception for errors on sensor management """ - BUS_ERROR = 0x01 + BUS_ERROR = 0x01 DATA_ERROR = 0x02 CRC_ERROR = 0x03 def __init__(self, error_code=None): self.error_code = error_code super().__init__(self.get_message()) - + def get_message(self): if self.error_code == SHT30Error.BUS_ERROR: return "Bus error" @@ -200,4 +201,3 @@ def get_message(self): return "CRC error" else: return "Unknown error" - From a5b885e6ed28894eaa971841076238f791ea0199 Mon Sep 17 00:00:00 2001 From: Bruce Duncan Date: Fri, 7 Jun 2019 19:50:33 +0100 Subject: [PATCH 4/5] Document measure_dp() --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index 3158598..1629802 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,17 @@ temperature, humidity = sensor.measure() print('Temperature:', temperature, 'ºC, RH:', humidity, '%') ``` +The `measure_dp()` method returns a similar tuple but with the dew point temperature (i.e. the temperature at which dew will form if the air is cooled adiabatically) instead of relative humidity. Both values are degrees Celsius. + +```python +from sht30 import SHT30 + +sensor = SHT30() + +temperature, dew_point = sensor.measure_dp() +print('Temperature:', temperature, '°C, dew point:', dew_point, '°C') +``` + There is another method, `measure_int()`, that returns 4 integer values, **no floating point operation is done**, designed for environments that doesn't support floating point operations, the four values are: From 7a94416ef8295c5ebced91a712ae591698d773c3 Mon Sep 17 00:00:00 2001 From: Bruce Duncan Date: Fri, 7 Jun 2019 19:50:54 +0100 Subject: [PATCH 5/5] Fix markdown, spelling, whitespace. Try to fix the markdown headers on github, some minor spelling and use degree symbol instead of masculine ordinal. --- README.md | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 1629802..74b4395 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,23 @@ -#SHT30 Sensor driver in micropython +# SHT30 Sensor driver in micropython Micropython driver for [SHT30 Shield](https://www.wemos.cc/product/sht30-shield.html) for [Wemos D1 Mini (and PRO)](https://www.wemos.cc/product/d1-mini-pro.html). -The driver has been tested on Wemos D1 mini PRO, but It should work on whatever other micropython board, if anyone find problems in other boards, please open an issue and We'll see. +The driver has been tested on Wemos D1 mini PRO, but it should work on whatever other micropython board, if anyone find problems in other boards, please open an issue and we'll see. -##Motivation +## Motivation The SHT30 shield for ESP8266 board Wemos D1 Mini has an Arduino driver but not a micropython one. -##References: +## References: * [Sensor Datasheet](https://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/2_Humidity_Sensors/Sensirion_Humidity_Sensors_SHT3x_Datasheet_digital.pdf) * [Arduino driver](https://github.com/wemos/WEMOS_SHT3x_Arduino_Library) * [SHT30 C Code Examples](https://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/11_Sample_Codes_Software/Humidity_Sensors/Sensirion_Humidity_Sensors_SHT3x_Sample_Code_V2.pdf) from sensor manufacturer -##Examples of use: +## Examples of use: -###How to get the temperature and relative humidity: +### How to get the temperature and relative humidity: -The `measure()` method returns a tuple with the temperature in celsius grades and the relative humidity in percentage. +The `measure()` method returns a tuple with the temperature in degrees Celsius and the relative humidity in percentage. If the measurement cannot be performed then an exception is raised (`SHT30Error`) ```python @@ -27,7 +27,7 @@ sensor = SHT30() temperature, humidity = sensor.measure() -print('Temperature:', temperature, 'ºC, RH:', humidity, '%') +print('Temperature:', temperature, '°C, RH:', humidity, '%') ``` The `measure_dp()` method returns a similar tuple but with the dew point temperature (i.e. the temperature at which dew will form if the air is cooled adiabatically) instead of relative humidity. Both values are degrees Celsius. @@ -41,12 +41,12 @@ temperature, dew_point = sensor.measure_dp() print('Temperature:', temperature, '°C, dew point:', dew_point, '°C') ``` -There is another method, `measure_int()`, that returns 4 integer values, **no floating point operation is done**, designed -for environments that doesn't support floating point operations, the four values are: +There is another method, `measure_int()`, that returns 4 integer values, **no floating point operation is done**, designed +for environments that doesn't support floating point operations, the four values are: Temperature (integer part), Temperature (decimal part), RH (integer part), RH (decimal part) -For intance, if the `measure()` method returns `(21.5623, 32.0712)` the `measure_int()` method would return: `(24, 56, 32, 7)` The decimal +For instance, if the `measure()` method returns `(21.5623, 32.0712)` the `measure_int()` method would return: `(24, 56, 32, 7)` The decimal part is limited to 2 decimal digits. ```python @@ -55,7 +55,7 @@ t_int, t_dec, h_int, h_dec = sensor.measure_int() print('Temperature: %i.%02i °C, RH: %i.%02i %%' % (t_int, t_dec, h_int, h_dec)) ``` -Both methods allow a `raw` param that when It's `True` returns the sensor measurement as-is, It's a `bytearray(6)` with the format defined in the sensor datasheet document. +Both methods allow a `raw` param that when it's `True` returns the sensor measurement as-is, it's a `bytearray(6)` with the format defined in the sensor datasheet document. ```python raw_measure = sensor.measure(raw=True) @@ -63,7 +63,7 @@ raw_measure = sensor.measure(raw=True) print('Sensor measurement', raw_measure) ``` -###Check if shield is connected +### Check if shield is connected ```python from sht30 import SHT30 @@ -74,7 +74,7 @@ print('Is connected:', sensor.is_present()) ``` -###Read sensor status +### Read sensor status Check the [Sensor Datasheet](https://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/2_Humidity_Sensors/Sensirion_Humidity_Sensors_SHT3x_Datasheet_digital.pdf) for further info about sensor status register ```python @@ -85,13 +85,13 @@ sensor = SHT30() print('Status register:', bin(sensor.status())) print('Single bit check, HEATER_MASK:', bool(sensor.status() & SHT30.HEATER_MASK)) -#The status register can be cleared with +# The status register can be cleared with sensor.clear_status() ``` -###Reset the sensor +### Reset the sensor The driver allows a soft reset of the sensor @@ -105,9 +105,9 @@ sensor.reset() -###Error management +### Error management -When the driver cannot access to the measurement an exception `SHT30Error` is raised +When the driver cannot access the sensor an `SHT30Error` exception is raised ```python from sht30 import SHT30 @@ -119,5 +119,4 @@ try: except SHT30Error as ex: print('Error:', ex) - ```