Pico W Micro Python for SHT30 Temp/Humidity HELP!

RP2040 based microcontroller boards running MicroPython.
Target audience: MicroPython users with an RP2040 boards.
This does not include conventional Linux-based Raspberry Pi boards.
Rissy
Posts: 116
Joined: Sun Aug 14, 2022 8:15 am

Re: Pico W Micro Python for SHT30 Temp/Humidity HELP!

Post by Rissy » Sun Aug 14, 2022 4:30 pm

First off, I just want to say once again, a big thank you for continuing to help me out here. It's very much appreciated. Sounds like this code was due an update!

So this is what I have in Thonny now.

sht30.py

Code: Select all

from machine import I2C, Pin
import time

__version__ = '0.2.3'
__author__ = 'Roberto Sánchez'
__license__ = "Apache License 2.0. https://www.apache.org/licenses/LICENSE-2.0"

# I2C address B 0x44 ADDR (pin 2) not connected to VDD
DEFAULT_I2C_ADDRESS = 0x44

# I2C address B 0x45 ADDR (pin 2) connected to VDD
#DEFAULT_I2C_ADDRESS = 0x45


class SHT30:
    """
    SHT30 sensor driver in pure python based on I2C bus

    References:
    * https://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/2_Humidity_Sensors/Sensirion_Humidity_Sensors_SHT3x_Datasheet_digital.pdf  # NOQA
    * https://www.wemos.cc/sites/default/files/2016-11/SHT30-DIS_datasheet.pdf
    * https://github.com/wemos/WEMOS_SHT3x_Arduino_Library
    * https://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/11_Sample_Codes_Software/Humidity_Sensors/Sensirion_Humidity_Sensors_SHT3x_Sample_Code_V2.pdf
    """
    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

    # MSB = 0x2C LSB = 0x06 Repeatability = High, Clock stretching = enabled
    MEASURE_CMD = b'\x2C\x10'
    STATUS_CMD = b'\xF3\x2D'
    RESET_CMD = b'\x30\xA2'
    CLEAR_STATUS_CMD = b'\x30\x41'
    ENABLE_HEATER_CMD = b'\x30\x6D'
    DISABLE_HEATER_CMD = b'\x30\x66'

    def __init__(self, i2c=None, delta_temp=0, delta_hum=0, i2c_address=DEFAULT_I2C_ADDRESS):
        if i2c is None:
            raise ValueError('An I2C object is required.')
        self.i2c = i2c
        self.i2c_addr = i2c_address
        self.set_delta(delta_temp, delta_hum)
        time.sleep_ms(50)

    def is_present(self):
        """
        Return true if the sensor is correctly conneced, False otherwise
        """
        return self.i2c_addr in self.i2c.scan()

    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
            for _ in range(8, 0, -1):
                if crc & 0x80:
                    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
        """
        try:
            self.i2c.writeto(self.i2c_addr, cmd_request)
            if not response_size:
                return
            time.sleep_ms(read_delay_ms)
            data = self.i2c.readfrom(self.i2c_addr, response_size)

            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)
            if data == bytearray(response_size):
                raise SHT30Error(SHT30Error.DATA_ERROR)
            return data
        except OSError as ex:
            raise SHT30Error(SHT30Error.BUS_ERROR)
        except Exception as ex:
            raise ex

    def clear_status(self):
        """
        Clear the status register
        """
        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)

    def status(self, raw=False):
        """
        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)

        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
        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)

        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
        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
        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:
            return data
        aux = (data[0] << 8 | data[1]) * 175
        t_int = (aux // 0xffff) - 45
        t_dec = (aux % 0xffff * 100) // 0xffff
        aux = (data[3] << 8 | data[4]) * 100
        h_int = aux // 0xffff
        h_dec = (aux % 0xffff * 100) // 0xffff
        return t_int, t_dec, h_int, h_dec


class SHT30Error(Exception):
    """
    Custom exception for errors on sensor management
    """
    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"
        elif self.error_code == SHT30Error.DATA_ERROR:
            return "Data error"
        elif self.error_code == SHT30Error.CRC_ERROR:
            return "CRC error"
        else:
            return "Unknown error"

sht_read.py

Code: Select all

from machine import SoftI2C, Pin
import sht30

i2c=SoftI2C(sda=Pin(4), scl=Pin(5))
sht=sht30.SHT30(i2c=i2c, i2c_address=64)

sht.measure()


#from sht30 import SHT30

sensor = SHT30()

temperature, humidity = sensor.measure()
#temperature = sensor.measure(t_celsius)
#humidity = sensor.measure(rh)
print("Temperature:", temperature, "ºC, RH:", humidity, "%")

#or
#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))
#
#or
#raw_measure = sensor.measure(raw=True)
#print('Sensor measurement', raw_measure)
#
###Check if shield is connected
#from sht30 import SHT30
#sensor = SHT30()
print('Is connected:', sensor.is_present())

###Read sensor status
#from sht30 import SHT30
#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
#sensor.clear_status()

###Reset the sensor
#from sht30 import SHT30
#sensor = SHT30()
#sensor.reset()

###Error management
#from sht30 import SHT30
#sensor = SHT30()

#try:
#    t, h = sensor.measure()
#except SHT30Error as ex:
#    print('Error:', ex)
Running sht_read.py gives me the following result in Thonny:

%Run -c $EDITOR_CONTENT
Traceback (most recent call last):
File "<stdin>", line 7, in <module>
File "sht30.py", line 134, in measure
File "sht30.py", line 98, in send_cmd
SHT30Error: Bus error

User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: Pico W Micro Python for SHT30 Temp/Humidity HELP!

Post by Roberthh » Sun Aug 14, 2022 4:33 pm

The address was wrong.

Code: Select all

from machine import SoftI2C, Pin
import sht30

i2c=SoftI2C(sda=Pin(4), scl=Pin(5))
sht=sht30.SHT30(i2c=i2c, , i2c_address=68)

sht.measure()

Rissy
Posts: 116
Joined: Sun Aug 14, 2022 8:15 am

Re: Pico W Micro Python for SHT30 Temp/Humidity HELP!

Post by Rissy » Sun Aug 14, 2022 4:47 pm

Roberthh wrote:
Sun Aug 14, 2022 4:33 pm
The address was wrong.

Code: Select all

from machine import SoftI2C, Pin
import sht30

i2c=SoftI2C(sda=Pin(4), scl=Pin(5))
sht=sht30.SHT30(i2c=i2c, , i2c_address=68)

sht.measure()
Ah of course.

Right. Now i have the following in sht_read.py

Code: Select all

from machine import SoftI2C, Pin
import sht30

i2c=SoftI2C(sda=Pin(4), scl=Pin(5))
sht=sht30.SHT30(i2c=i2c, i2c_address=68)

sensor = sht.measure()


#from sht30 import SHT30

#sensor = SHT30()

#temperature, humidity = sensor.measure()
#temperature, humidity = sht.measure()
temperature, humidity = sensor
#temperature = sensor.measure(t_celsius)
#humidity = sensor.measure(rh)
#print("Temperature:", temperature, "ºC, RH:", humidity, "%")
print('Temperature: {:4.2f}ºC'.format(temperature))
print('Humidity: {:4.2f}%'.format(humidity))

#or
#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))
#
#or
#raw_measure = sensor.measure(raw=True)
#print('Sensor measurement', raw_measure)
#
###Check if shield is connected
#from sht30 import SHT30
#sensor = SHT30()
#                                     print('Is connected:', sensor.is_present())
print('Is connected:', sensor.is_present())

###Read sensor status
#from sht30 import SHT30
#sensor = SHT30()
#                                     print('Status register:', bin(sensor.status()))
#                                     print('Single bit check, HEATER_MASK:', bool(sensor.status() & SHT30.HEATER_MASK))
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
#sensor.clear_status()

###Reset the sensor
#from sht30 import SHT30
#sensor = SHT30()
#sensor.reset()

###Error management
#from sht30 import SHT30
#sensor = SHT30()

#try:
#    t, h = sensor.measure()
#except SHT30Error as ex:
#    print('Error:', ex)

You'll see I've had a play with some of the other lines:

Code: Select all

print('Is connected:', sensor.is_present())
print('Status register:', bin(sensor.status()))
print('Single bit check, HEATER_MASK:', bool(sensor.status() & SHT30.HEATER_MASK))
These aren't working at all. Is this possible or not?

BUT.

I do now get the following result with these lines hashed out:

%Run -c $EDITOR_CONTENT
Temperature: 28.94ºC
Humidity: 35.58%

So good result even without resistors! :)

User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: Pico W Micro Python for SHT30 Temp/Humidity HELP!

Post by Roberthh » Sun Aug 14, 2022 5:13 pm

So the driver is working. The sensor has most probably the resistors inside.

Rissy
Posts: 116
Joined: Sun Aug 14, 2022 8:15 am

Re: Pico W Micro Python for SHT30 Temp/Humidity HELP!

Post by Rissy » Sun Aug 14, 2022 5:21 pm

Roberthh wrote:
Sun Aug 14, 2022 5:13 pm
So the driver is working. The sensor has most probably the resistors inside.
The driver is most certainly working now, at least for Temerature and Humidity. Not for anything else. (is it possible to make the other things in the driver work?)

A BIG THANK YOU to you good sir for helping me out with this. :)

The simple fact is that I would NEVER have got that working without someone's help.

That level of understanding of coding etc is simply out of my reach.

I'm much more of a higher level type of guy, and I leave the lower level stuff to people much cleverer than myself.

You, are one of those cleverer people than me for sure!

User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: Pico W Micro Python for SHT30 Temp/Humidity HELP!

Post by Roberthh » Sun Aug 14, 2022 6:45 pm

You're welcome. Looking back I could have come to a working version a little bit earlier. But taking the I2C instantiation out of the driver is anyhow the better approach, offering freedom in selecting different I2C interfaces.

Rissy
Posts: 116
Joined: Sun Aug 14, 2022 8:15 am

Re: Pico W Micro Python for SHT30 Temp/Humidity HELP!

Post by Rissy » Tue Aug 16, 2022 3:10 pm

Thanks Roberthh,

Is there any way of defining the "default address" within the read file instead of the driver file which then the driver takes onboard to use as the defined address rather than defining the address within the driver file itself? (if you get me!?)

Are the other options which this driver (originally) allowed for, still useable by me if this driver was fully fixed?

User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: Pico W Micro Python for SHT30 Temp/Humidity HELP!

Post by Roberthh » Tue Aug 16, 2022 3:22 pm

A default address is defined in the driver. You can override this address when creating the device instance in the script using the driver. In that script, you can of course set your own default.

About the second question: You can of course use all methods provided by the SHT30 driver.

Rissy
Posts: 116
Joined: Sun Aug 14, 2022 8:15 am

Re: Pico W Micro Python for SHT30 Temp/Humidity HELP!

Post by Rissy » Tue Aug 16, 2022 4:29 pm

Roberthh wrote:
Tue Aug 16, 2022 3:22 pm
A default address is defined in the driver. You can override this address when creating the device instance in the script using the driver. In that script, you can of course set your own default.
That's great! How would one do that? Can you help me?
Roberthh wrote:
Tue Aug 16, 2022 3:22 pm
About the second question: You can of course use all methods provided by the SHT30 driver.
Great again! But of course right now, they are not working. I've had to # the appropriate lines out otherwise I get errors. Can you help me fix each one?

As a reminder these are the operations from the code in my earlier post which are still # out.

###Check if shield is connected

###Read sensor status

#The status register can be cleared with

###Reset the sensor

###Error management

Rissy
Posts: 116
Joined: Sun Aug 14, 2022 8:15 am

Re: Pico W Micro Python for SHT30 Temp/Humidity HELP!

Post by Rissy » Wed Aug 17, 2022 7:40 am

Rissy wrote:
Tue Aug 16, 2022 4:29 pm
###Check if shield is connected

###Read sensor status

#The status register can be cleared with

###Reset the sensor

###Error management
Of course, the invitation for anyone, Robberthh or a.another, is an open invitation to take a look at the driver file and my read file if anyone is able to point out how to fix and make work the above areas of code remaining hashed out because they don't work right now. Unfortunately it's beyond my capability to do so.

Post Reply