Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SHT31.py #6

Open
nadanks7 opened this issue Jul 23, 2015 · 23 comments
Open

SHT31.py #6

nadanks7 opened this issue Jul 23, 2015 · 23 comments

Comments

@nadanks7
Copy link

In case you were curious I've just recently made a similar module for the new SHT31 sensor, and tested it on raspberry pi 2 model b.
I'm posting here in part to give credit here; since I used your SHT21.py code as a base and modified from there (based on sensirions sht31 datasheet) to make it work for the SHT31
The code isn't particularly clean yet and I'm definitely an elementary programmer, but it works. If you wanted to see the code I can post it here.

@jaques
Copy link
Owner

jaques commented Jul 25, 2015

Hi I'm glad to hear that the code was useful to get you started. I think that it would be worthwhile posting your version of the code here so that others might be able to pick it up and use it.

Richard.

@nadanks7
Copy link
Author

def __enter__(self):
    """used to enable python's with statement support"""
    return self

def __exit__(self, type, value, traceback):
    """with support"""
    self.close()

@staticmethod
def _calculate_checksum(data, number_of_bytes):
    """4.12 Checksum Calculation"""
    # CRC
    POLYNOMIAL = 0x131  # //P(x)=x^8+x^5+x^4+1 = 100110001
    crc = 0xFF
    # calculates 8-Bit checksum with given polynomial
    for byteCtr in range(number_of_bytes):
        crc ^= (ord(data[byteCtr]))
        for bit in range(8, 0, -1):
            if crc & 0x80:
                crc = (crc << 1) ^ POLYNOMIAL
            else:
                crc = (crc << 1)
    return crc

@staticmethod
def _get_temperature_from_buffer(data):
    """This function reads the first two bytes of data and
    returns the temperature in C by using the following function:
    T = =45 + (175 * (ST/2^16))
    where ST is the value from the sensor
    """
    unadjusted = (ord(data[0]) << 8) + ord(data[1])
# print unadjusted
    unadjusted &= SHT31._STATUS_BITS_MASK  # zero the status bits
# print unadjusted
    unadjusted *= 175.0
    unadjusted /= 1 << 16  # divide by 2^16
    unadjusted -= 45
    return unadjusted

@staticmethod
def _get_humidity_from_buffer(data):
    """This function reads the first two bytes of data and returns
    the relative humidity in percent by using the following function:
    RH = (100 * (SRH / 2 ^16))
    where SRH is the value read from the sensor
    """
    unadjusted = (ord(data[3]) << 8) + ord(data[4])
# print unadjusted
    unadjusted &= SHT31._STATUS_BITS_MASK  # zero the status bits
# print unadjusted
    unadjusted *= 100.0
    unadjusted /= 1 << 16  # divide by 2^16
    unadjusted -= 0
    return unadjusted

if name == "main":
try:
with SHT31(1) as sht31:
print sht31.check_heater_status()
sht31.turn_heater_on()
print sht31.check_heater_status()
sht31.turn_heater_off()
print sht31.check_heater_status()
print "Temperature: %s" % sht31.Measure_Temp_and_Humidity()[0]
print "Humidity: %s" % sht31.Measure_Temp_and_Humidity()[1]

except IOError, e:
    print e
    print "Error creating connection to i2c.  This must be run as root"

@nadanks7
Copy link
Author

Returns temp and humidity as a list, with temp as index 0 and humidity as index 1. I've also added in heater functionality.

@jimbowarrior
Copy link

hello,

have you full code to read a SHT31 sensor ?

Thank you

@nadanks7
Copy link
Author

The code I posted works to read temp and humidity, and turn on and off
heater _ it was tested on a pi2 model b
On Dec 23, 2015 4:23 PM, "jimbowarrior" [email protected] wrote:

hello,

have you full code to read a SHT31 sensor ?

Thank you


Reply to this email directly or view it on GitHub
#6 (comment).

@nadanks7
Copy link
Author

Now that I'm looking at it closely it looks like the code that actually
posted is incomplete. I have the complete code but no access to it until
after the new year.
On Dec 24, 2015 7:54 PM, "Nathan Danks" [email protected] wrote:

The code I posted works to read temp and humidity, and turn on and off
heater _ it was tested on a pi2 model b
On Dec 23, 2015 4:23 PM, "jimbowarrior" [email protected] wrote:

hello,

have you full code to read a SHT31 sensor ?

Thank you


Reply to this email directly or view it on GitHub
#6 (comment).

@jimbowarrior
Copy link

Ok thank you
I'm waiting for you :-)

@nadanks7
Copy link
Author

#!/usr/bin/python
import fcntl
import time
import unittest

class SHT31:
"""Class to read temperature and humidity from SHT31, much of class was
derived from:
https://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/Humidity_and_Temperature_Sensors/Sensirion_Humidity_and_Temperature_Sensors_SHT3x_Datasheet_digital.pdf
and jacques code from github"""

# control constants
_RESET_MSB = 0x30
_RESET_LSB = 0xA2
_HEATER_MSB = 0x30
_HEATER_LSB_ON = 0x6D
_HEATER_LSB_OFF = 0x66
_STATUS_MSB = 0xF3
_STATUS_LSB = 0x2D
_I2C_ADDRESS = 0x44
_TRIGGER_MSB = 0x2C
_TRIGGER_LSB = 0x06
_STATUS_BITS_MASK = 0xFFFC

# From: /linux/i2c-dev.h
I2C_SLAVE = 0x0703
I2C_SLAVE_FORCE = 0x0706

# datasheet (v0.93), page 5, table 4
_MEASUREMENT_WAIT_TIME = 0.015  # (datasheet: typ=12.5, max=15)

def __init__(self, device_number = 1):
    """Opens the i2c device (assuming that the kernel modules have been
    loaded).  Note that this has only been tested on first revision
    raspberry pi where the device_number = 0, but it should work
    where device_number=1"""
    self.i2c = open('/dev/i2c-%s' % device_number, 'r+', 0)
    fcntl.ioctl(self.i2c, self.I2C_SLAVE, 0x44)
    # self.i2c.write(chr(self._SOFTRESET))
    time.sleep(0.050)

def soft_reset(self):

"""Performs Soft Reset on SHT31 chip"""
reset = chr(self._RESET_MSB) + chr(self._RESET_LSB)
self.i2c.write(reset)

def check_heater_status(self):

"""Checks the status of the heater. Returns False if off and True if on"""
status = chr(self._STATUS_MSB) + chr(self._STATUS_LSB)
self.i2c.write(status)
stats = self.i2c.read(3)
#print str(ord(stats[0]))
#print str(ord(stats[1]))
#print str(ord(stats[2]))
#print self._calculate_checksum(stats,2)
bit13 = ord(stats[0])&(1<<5)!=0
#print bit13
if self._calculate_checksum(stats,2) == ord(stats[2]):
return bit13
else:
return "status read failure"

def turn_heater_on(self):

"""Enables Heater"""
heater_enable = chr(self._HEATER_MSB) + chr(self._HEATER_LSB_ON)
self.i2c.write(heater_enable)

def turn_heater_off(self):

"""Disables Heater"""
heater_disable = chr(self._HEATER_MSB) + chr(self._HEATER_LSB_OFF)
self.i2c.write(heater_disable)

def Measure_Temp_and_Humidity(self):
    """Reads the temperature and humidity - note that this call blocks

the program for 15ms"""
Trigger = chr(self._TRIGGER_MSB) + chr(self._TRIGGER_LSB)
tempdata=[]
humiddata=[]
self.i2c.write(Trigger)
time.sleep(self._MEASUREMENT_WAIT_TIME)
data = self.i2c.read(6)
tempdata += data[0]
tempdata += data[1]
humiddata += data[3]
humiddata += data[4]
if self._calculate_checksum(tempdata, 2) == ord(data[2]): #Checks
temperature checksum
if self._calculate_checksum(humiddata, 2) == ord(data[5]): #Checks
Humidity checksum
return [self._get_temperature_from_buffer(data),
self._get_humidity_from_buffer(data)] #Return List of temperature and
Humidity
else:
return [0,0]

def close(self):
    """Closes the i2c connection"""
    self.i2c.close()

def __enter__(self):
    """used to enable python's with statement support"""
    return self

def __exit__(self, type, value, traceback):
    """with support"""
    self.close()

@staticmethod
def _calculate_checksum(data, number_of_bytes):
    """4.12 Checksum Calculation"""
    # CRC
    POLYNOMIAL = 0x131  # //P(x)=x^8+x^5+x^4+1 = 100110001
    crc = 0xFF
    # calculates 8-Bit checksum with given polynomial
    for byteCtr in range(number_of_bytes):
        crc ^= (ord(data[byteCtr]))
        for bit in range(8, 0, -1):
            if crc & 0x80:
                crc = (crc << 1) ^ POLYNOMIAL
            else:
                crc = (crc << 1)
    return crc

@staticmethod
def _get_temperature_from_buffer(data):
    """This function reads the first two bytes of data and
    returns the temperature in C by using the following function:
    T = =45 + (175 * (ST/2^16))
    where ST is the value from the sensor
    """
    unadjusted = (ord(data[0]) << 8) + ord(data[1])

print unadjusted

    unadjusted &= SHT31._STATUS_BITS_MASK  # zero the status bits

print unadjusted

    unadjusted *= 175.0
    unadjusted /= 1 << 16  # divide by 2^16
    unadjusted -= 45
    return unadjusted

@staticmethod
def _get_humidity_from_buffer(data):
    """This function reads the first two bytes of data and returns
    the relative humidity in percent by using the following function:
    RH = (100 * (SRH / 2 ^16))
    where SRH is the value read from the sensor
    """
    unadjusted = (ord(data[3]) << 8) + ord(data[4])

print unadjusted

    unadjusted &= SHT31._STATUS_BITS_MASK  # zero the status bits

print unadjusted

    unadjusted *= 100.0
    unadjusted /= 1 << 16  # divide by 2^16
    unadjusted -= 0
    return unadjusted

if name == "main":
try:
with SHT31(1) as sht31:
print sht31.check_heater_status()
sht31.turn_heater_on()
print sht31.check_heater_status()
sht31.turn_heater_off()
print sht31.check_heater_status()
print "Temperature: %s" % sht31.Measure_Temp_and_Humidity()[0]
print "Humidity: %s" % sht31.Measure_Temp_and_Humidity()[1]

except IOError, e:
    print e
    print "Error creating connection to i2c.  This must be run as root"

On Fri, Dec 25, 2015 at 3:39 AM, jimbowarrior [email protected]
wrote:

Ok thank you
I'm waiting for you :-)


Reply to this email directly or view it on GitHub
#6 (comment).

@nadanks7
Copy link
Author

The code above appears much more complete.

It is currently running on my pi2 model b, so it definitely works; although honestly at this point I don't recall all of the steps necessary to set up the pi to handle the i2c connection correctly... although I'm sure I took more steps than are actually necessary, since I set it up to work on a little touchscreen, and wrote a little code to log the temp / rh.

The temperature and humidity in the room is currently 23C / 18% RH!

@jimbowarrior
Copy link

Thank you, I have a model B.
At this time I get and error message a loop if

if self._calculate_checksum(tempdata, 2) == ord(data[2]): #Checks
temperature checksum

temperature checksum
^
SyntaxError: invalid syntax

@nadanks7
Copy link
Author

There's a copy paste issue going on here - the comment line #checks
temperature checksum should all be on the same line, probably one line
below the if statement.

"temperature checksum" by itself is an invalid command, and thus gets
parsed as invalid syntax.

Not sure if my attachment will stick, but I've attached the actual .py file.

Not sure if you're familiar with python syntax at all, but comments are
denoted by "#"; so #checks temperature checksum is a comment. There
probably a few of these copy / paste issues throughout the document.

On Mon, Dec 28, 2015 at 12:53 PM, jimbowarrior [email protected]
wrote:

Thank you, I have a model B.
At this time I get and error message a loof if

if self._calculate_checksum(tempdata, 2) == ord(data[2]): #Checks
temperature checksum

temperature checksum
^
SyntaxError: invalid syntax


Reply to this email directly or view it on GitHub
#6 (comment).

#!/usr/bin/python
import fcntl
import time
import unittest

class SHT31:
"""Class to read temperature and humidity from SHT31, much of class was
derived from:
http://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/Humidity/Sensirion_Humidity_SHT21_Datasheet_V3.pdf
and Martin Steppuhn's code from http://www.emsystech.de/raspi-sht21"""

# control constants
_RESET_MSB = 0x30
_RESET_LSB = 0xA2
_HEATER_MSB = 0x30
_HEATER_LSB_ON = 0x6D
_HEATER_LSB_OFF = 0x66
_STATUS_MSB = 0xF3
_STATUS_LSB = 0x2D
_I2C_ADDRESS = 0x44
_TRIGGER_MSB = 0x2C
_TRIGGER_LSB = 0x06
_STATUS_BITS_MASK = 0xFFFC

# From: /linux/i2c-dev.h
I2C_SLAVE = 0x0703
I2C_SLAVE_FORCE = 0x0706

# datasheet (v0.93), page 5, table 4
_MEASUREMENT_WAIT_TIME = 0.015  # (datasheet: typ=12.5, max=15)

def __init__(self, device_number = 1):
    """Opens the i2c device (assuming that the kernel modules have been
    loaded).  Note that this has only been tested on first revision
    raspberry pi where the device_number = 0, but it should work
    where device_number=1"""
    self.i2c = open('/dev/i2c-%s' % device_number, 'r+', 0)
    fcntl.ioctl(self.i2c, self.I2C_SLAVE, 0x44)
    # self.i2c.write(chr(self._SOFTRESET))
    time.sleep(0.050)

def soft_reset(self):
"""Performs Soft Reset on SHT31 chip"""
reset = chr(self._RESET_MSB) + chr(self._RESET_LSB)
self.i2c.write(reset)

def check_heater_status(self):
"""Checks the status of the heater. Returns False if off and True if on"""
status = chr(self._STATUS_MSB) + chr(self._STATUS_LSB)
self.i2c.write(status)
stats = self.i2c.read(3)
#print str(ord(stats[0]))
#print str(ord(stats[1]))
#print str(ord(stats[2]))
#print self._calculate_checksum(stats,2)
bit13 = ord(stats[0])&(1<<5)!=0
#print bit13
if self._calculate_checksum(stats,2) == ord(stats[2]):
    return bit13
else:
    return "status read failure"

def turn_heater_on(self):
"""Enables Heater"""
heater_enable = chr(self._HEATER_MSB) + chr(self._HEATER_LSB_ON)
self.i2c.write(heater_enable)

def turn_heater_off(self):
"""Disables Heater"""
heater_disable = chr(self._HEATER_MSB) + chr(self._HEATER_LSB_OFF)
self.i2c.write(heater_disable)


def Measure_Temp_and_Humidity(self):    
    """Reads the temperature and humidity - note that this call blocks the program for 15ms"""
Trigger = chr(self._TRIGGER_MSB) + chr(self._TRIGGER_LSB)
tempdata=[]
humiddata=[]
    self.i2c.write(Trigger)
    time.sleep(self._MEASUREMENT_WAIT_TIME)
    data = self.i2c.read(6)
tempdata += data[0]
tempdata += data[1]
humiddata += data[3]
humiddata += data[4]
    if self._calculate_checksum(tempdata, 2) == ord(data[2]):                       #Checks temperature checksum
    if self._calculate_checksum(humiddata, 2) == ord(data[5]):                      #Checks Humidity checksum
            return [self._get_temperature_from_buffer(data), self._get_humidity_from_buffer(data)]      #Return List of temperature and Humidity
else:
    return [0,0]

def close(self):
    """Closes the i2c connection"""
    self.i2c.close()

def __enter__(self):
    """used to enable python's with statement support"""
    return self

def __exit__(self, type, value, traceback):
    """with support"""
    self.close()

@staticmethod
def _calculate_checksum(data, number_of_bytes):
    """4.12 Checksum Calculation"""
    # CRC
    POLYNOMIAL = 0x131  # //P(x)=x^8+x^5+x^4+1 = 100110001
    crc = 0xFF
    # calculates 8-Bit checksum with given polynomial
    for byteCtr in range(number_of_bytes):
        crc ^= (ord(data[byteCtr]))
        for bit in range(8, 0, -1):
            if crc & 0x80:
                crc = (crc << 1) ^ POLYNOMIAL
            else:
                crc = (crc << 1)
    return crc

@staticmethod
def _get_temperature_from_buffer(data):
    """This function reads the first two bytes of data and
    returns the temperature in C by using the following function:
    T = =45 + (175 * (ST/2^16))
    where ST is the value from the sensor
    """
    unadjusted = (ord(data[0]) << 8) + ord(data[1])
# print unadjusted
    unadjusted &= SHT31._STATUS_BITS_MASK  # zero the status bits
# print unadjusted
    unadjusted *= 175.0
    unadjusted /= 1 << 16  # divide by 2^16
    unadjusted -= 45
    return unadjusted

@staticmethod
def _get_humidity_from_buffer(data):
    """This function reads the first two bytes of data and returns
    the relative humidity in percent by using the following function:
    RH = (100 * (SRH / 2 ^16))
    where SRH is the value read from the sensor
    """
    unadjusted = (ord(data[3]) << 8) + ord(data[4])
# print unadjusted
    unadjusted &= SHT31._STATUS_BITS_MASK  # zero the status bits
# print unadjusted
    unadjusted *= 100.0
    unadjusted /= 1 << 16  # divide by 2^16
    unadjusted -= 0
    return unadjusted

if name == "main":
try:
with SHT31(1) as sht31:
print sht31.check_heater_status()
sht31.turn_heater_on()
print sht31.check_heater_status()
sht31.turn_heater_off()
print sht31.check_heater_status()
print "Temperature: %s" % sht31.Measure_Temp_and_Humidity()[0]
print "Humidity: %s" % sht31.Measure_Temp_and_Humidity()[1]

except IOError, e:
    print e
    print "Error creating connection to i2c.  This must be run as root"

@jimbowarrior
Copy link

could you please send me your file to [email protected]

Thanks

@jaques
Copy link
Owner

jaques commented Dec 29, 2015

Hi,

This looks great. I don't have a sensor, so I've got no idea if this will even run, but here's another version of that code moved around a bit and using python's struct module. If possible someone could run it and let me know if it works (I'd be surprised if it worked first time), I'd really appreciate it.

#!/usr/bin/python
import fcntl
import struct
import time


class SHT31:
    """Class to read temperature and humidity from SHT31, much of class was
    this is based on code from nadanks7 who used
    https://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/Humidity_and_Temperature_Sensors/Sensirion_Humidity_and_Temperature_Sensors_SHT3x_Datasheet_digital.pdf
    as well as jaques/SHT21 code from github
    """

    # control constants
    _RESET = 0x30A2
    _HEATER_ON = 0x306D
    _HEATER_OFF = 0x3066
    _STATUS = 0xF32D
    _TRIGGER = 0x2C06
    _STATUS_BITS_MASK = 0xFFFC

    _I2C_ADDRESS = 0x44

    # From: /linux/i2c-dev.h
    _I2C_SLAVE = 0x0703
    _I2C_SLAVE_FORCE = 0x0706

    # datasheet (v0.93), page 5, table 4
    _MEASUREMENT_WAIT_TIME = 0.015  # (datasheet: typ=12.5, max=15)

    def __init__(self, device_number=1):
        """Opens the i2c device (assuming that the kernel modules have been
        loaded)."""
        self.i2c = open('/dev/i2c-%s' % device_number, 'r+', 0)
        fcntl.ioctl(self.i2c, self._I2C_SLAVE, 0x44)
        time.sleep(0.050)

    def soft_reset(self):
        """Performs Soft Reset on SHT31 chip"""
        self.write(self._RESET)

    def check_heater_status(self):
        """Checks the status of the heater. Returns False if off and True if on"""
        self.write(self._STATUS)

        stats, checksum = struct.unpack(">HB", self.i2c.read(3))
        bit13 = stats & (1 << 13) != 0
        if self._calculate_checksum(stats) == checksum:
            return bit13
        else:
            return "status read failure"

    def turn_heater_on(self):
        """Enables Heater"""
        self.write(self._HEATER_ON)

    def turn_heater_off(self):
        """Disables Heater"""
        self.write(self._HEATER_OFF)

    def get_temp_and_humidity(self):
        """Reads the temperature and humidity - note that this call blocks
        the program for 15ms"""
        self.write(self._TRIGGER)
        time.sleep(self._MEASUREMENT_WAIT_TIME)
        data = self.i2c.read(6)

        temp_data, temp_checksum, humidity_data, humidity_checksum = struct.unpack(">HBHB", data)

        #  returns a tuple of (temperature, humidity)
        if self._calculate_checksum(temp_data) == temp_checksum and \
           self._calculate_checksum(humidity_data) == humidity_checksum:
            return self._get_temperature(temp_data), self._get_humidity(humidity_data)
        else:
            return 0, 0

    def write(self, value):
        self.i2c.write(struct.pack(">H", value))

    def close(self):
        """Closes the i2c connection"""
        self.i2c.close()

    def __enter__(self):
        """used to enable python's with statement support"""
        return self

    def __exit__(self, *exc_info):
        """with support"""
        self.close()

    @staticmethod
    def _calculate_checksum(value):
        """4.12 Checksum Calculation from an unsigned short input"""
        # CRC
        polynomial = 0x131  # //P(x)=x^8+x^5+x^4+1 = 100110001
        crc = 0xFF

        # calculates 8-Bit checksum with given polynomial
        for byteCtr in [ord(x) for x in struct.pack(">H", value)]:
            crc ^= byteCtr
            for bit in range(8, 0, -1):
                if crc & 0x80:
                    crc = (crc << 1) ^ polynomial
                else:
                    crc = (crc << 1)
        return crc

    @staticmethod
    def _get_temperature(unadjusted):
        """This function reads the first two bytes of data and
        returns the temperature in C by using the following function:
        T = 45 + (175 * (ST/2^16))
        where ST is the value from the sensor
        """
        unadjusted *= 175.0
        unadjusted /= 1 << 16  # divide by 2^16
        unadjusted -= 45
        return unadjusted

    @staticmethod
    def _get_humidity(unadjusted):
        """This function reads the first two bytes of data and returns
        the relative humidity in percent by using the following function:
        RH = (100 * (SRH / 2 ^16))
        where SRH is the value read from the sensor
        """
        unadjusted *= 100.0
        unadjusted /= 1 << 16  # divide by 2^16
        unadjusted -= 0
        return unadjusted


if __name__ == "__main__":
    try:
        with SHT31(1) as sht31:
            print sht31.check_heater_status()
            sht31.turn_heater_on()
            print sht31.check_heater_status()
            sht31.turn_heater_off()
            print sht31.check_heater_status()
            temperature, humidity = sht31.get_temp_and_humidity()
            print "Temperature: %s" % temperature
            print "Humidity: %s" % humidity
    except IOError, e:
        print e
        print "Error creating connection to i2c.  This must be run as root"

@jimbowarrior
Copy link

hello,

yes Natan danks gave me his code, it seems work fine, I need to check
tomorrow with another thermometer

thanks

2015-12-29 23:18 GMT+01:00 jaques [email protected]:

Hi,

This looks great. I don't have a sensor, so I've got no idea if this will
even run, but here's another version of that code moved around a bit and
using python's struct module. If possible someone could run it and let me
know if it works (I'd be surprised if it worked first time), I'd really
appreciate it.

#!/usr/bin/pythonimport fcntlimport structimport time

class SHT31:
"""Class to read temperature and humidity from SHT31, much of class was this is based on code from nadanks7 who used https://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/Humidity_and_Temperature_Sensors/Sensirion_Humidity_and_Temperature_Sensors_SHT3x_Datasheet_digital.pdf as well as jaques/SHT21 code from github """

# control constants
_RESET = 0x30A2
_HEATER_ON = 0x306D
_HEATER_OFF = 0x3066
_STATUS = 0xF32D
_TRIGGER = 0x2C06
_STATUS_BITS_MASK = 0xFFFC

_I2C_ADDRESS = 0x44

# From: /linux/i2c-dev.h
_I2C_SLAVE = 0x0703
_I2C_SLAVE_FORCE = 0x0706

# datasheet (v0.93), page 5, table 4
_MEASUREMENT_WAIT_TIME = 0.015  # (datasheet: typ=12.5, max=15)

def __init__(self, device_number=1):
    """Opens the i2c device (assuming that the kernel modules have been        loaded)."""
    self.i2c = open('/dev/i2c-%s' % device_number, 'r+', 0)
    fcntl.ioctl(self.i2c, self._I2C_SLAVE, 0x44)
    time.sleep(0.050)

def soft_reset(self):
    """Performs Soft Reset on SHT31 chip"""
    self.write(self._RESET)

def check_heater_status(self):
    """Checks the status of the heater. Returns False if off and True if on"""
    self.write(self._STATUS)

    stats, checksum = struct.unpack(">HB", self.i2c.read(3))
    bit13 = stats & (1 << 13) != 0
    if self._calculate_checksum(stats) == checksum:
        return bit13
    else:
        return "status read failure"

def turn_heater_on(self):
    """Enables Heater"""
    self.write(self._HEATER_ON)

def turn_heater_off(self):
    """Disables Heater"""
    self.write(self._HEATER_OFF)

def get_temp_and_humidity(self):
    """Reads the temperature and humidity - note that this call blocks        the program for 15ms"""
    self.write(self._TRIGGER)
    time.sleep(self._MEASUREMENT_WAIT_TIME)
    data = self.i2c.read(6)

    temp_data, temp_checksum, humidity_data, humidity_checksum = struct.unpack(">HBHB", data)

    #  returns a tuple of (temperature, humidity)
    if self._calculate_checksum(temp_data) == temp_checksum and \
       self._calculate_checksum(humidity_data) == humidity_checksum:
        return self._get_temperature(temp_data), self._get_humidity(humidity_data)
    else:
        return 0, 0

def write(self, value):
    self.i2c.write(struct.pack(">H", value))

def close(self):
    """Closes the i2c connection"""
    self.i2c.close()

def __enter__(self):
    """used to enable python's with statement support"""
    return self

def __exit__(self, *exc_info):
    """with support"""
    self.close()

@staticmethod
def _calculate_checksum(value):
    """4.12 Checksum Calculation from an unsigned short input"""
    # CRC
    polynomial = 0x131  # //P(x)=x^8+x^5+x^4+1 = 100110001
    crc = 0xFF

    # calculates 8-Bit checksum with given polynomial
    for byteCtr in [ord(x) for x in struct.pack(">H", value)]:
        crc ^= byteCtr
        for bit in range(8, 0, -1):
            if crc & 0x80:
                crc = (crc << 1) ^ polynomial
            else:
                crc = (crc << 1)
    return crc

@staticmethod
def _get_temperature(unadjusted):
    """This function reads the first two bytes of data and        returns the temperature in C by using the following function:        T = 45 + (175 * (ST/2^16))        where ST is the value from the sensor        """
    unadjusted *= 175.0
    unadjusted /= 1 << 16  # divide by 2^16
    unadjusted -= 45
    return unadjusted

@staticmethod
def _get_humidity(unadjusted):
    """This function reads the first two bytes of data and returns        the relative humidity in percent by using the following function:        RH = (100 * (SRH / 2 ^16))        where SRH is the value read from the sensor        """
    unadjusted *= 100.0
    unadjusted /= 1 << 16  # divide by 2^16
    unadjusted -= 0
    return unadjusted

if name == "main":
try:
with SHT31(1) as sht31:
print sht31.check_heater_status()
sht31.turn_heater_on()
print sht31.check_heater_status()
sht31.turn_heater_off()
print sht31.check_heater_status()
temperature, humidity = sht31.get_temp_and_humidity()
print "Temperature: %s" % temperature
print "Humidity: %s" % humidity
except IOError, e:
print e
print "Error creating connection to i2c. This must be run as root"


Reply to this email directly or view it on GitHub
#6 (comment).

@jimbowarrior
Copy link

hello,

everything works fine, another thermometer give me the same values,
negative value managed :

./sht31.py
False
True
False
Temperature: -0.587768554688
Humidity: 91.6809082031

I'm already contacted Nathan, I asked him to published his code, I'm
waiting for his confirmation.

thank you very much :-)

2015-12-30 0:14 GMT+01:00 TheJimboWarrior . [email protected]:

hello,

yes Natan danks gave me his code, it seems work fine, I need to check
tomorrow with another thermometer

thanks

2015-12-29 23:18 GMT+01:00 jaques [email protected]:

Hi,

This looks great. I don't have a sensor, so I've got no idea if this will
even run, but here's another version of that code moved around a bit and
using python's struct module. If possible someone could run it and let
me know if it works (I'd be surprised if it worked first time), I'd really
appreciate it.

#!/usr/bin/pythonimport fcntlimport structimport time

class SHT31:
"""Class to read temperature and humidity from SHT31, much of class was this is based on code from nadanks7 who used https://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/Humidity_and_Temperature_Sensors/Sensirion_Humidity_and_Temperature_Sensors_SHT3x_Datasheet_digital.pdf as well as jaques/SHT21 code from github """

# control constants
_RESET = 0x30A2
_HEATER_ON = 0x306D
_HEATER_OFF = 0x3066
_STATUS = 0xF32D
_TRIGGER = 0x2C06
_STATUS_BITS_MASK = 0xFFFC

_I2C_ADDRESS = 0x44

# From: /linux/i2c-dev.h
_I2C_SLAVE = 0x0703
_I2C_SLAVE_FORCE = 0x0706

# datasheet (v0.93), page 5, table 4
_MEASUREMENT_WAIT_TIME = 0.015  # (datasheet: typ=12.5, max=15)

def __init__(self, device_number=1):
    """Opens the i2c device (assuming that the kernel modules have been        loaded)."""
    self.i2c = open('/dev/i2c-%s' % device_number, 'r+', 0)
    fcntl.ioctl(self.i2c, self._I2C_SLAVE, 0x44)
    time.sleep(0.050)

def soft_reset(self):
    """Performs Soft Reset on SHT31 chip"""
    self.write(self._RESET)

def check_heater_status(self):
    """Checks the status of the heater. Returns False if off and True if on"""
    self.write(self._STATUS)

    stats, checksum = struct.unpack(">HB", self.i2c.read(3))
    bit13 = stats & (1 << 13) != 0
    if self._calculate_checksum(stats) == checksum:
        return bit13
    else:
        return "status read failure"

def turn_heater_on(self):
    """Enables Heater"""
    self.write(self._HEATER_ON)

def turn_heater_off(self):
    """Disables Heater"""
    self.write(self._HEATER_OFF)

def get_temp_and_humidity(self):
    """Reads the temperature and humidity - note that this call blocks        the program for 15ms"""
    self.write(self._TRIGGER)
    time.sleep(self._MEASUREMENT_WAIT_TIME)
    data = self.i2c.read(6)

    temp_data, temp_checksum, humidity_data, humidity_checksum = struct.unpack(">HBHB", data)

    #  returns a tuple of (temperature, humidity)
    if self._calculate_checksum(temp_data) == temp_checksum and \
       self._calculate_checksum(humidity_data) == humidity_checksum:
        return self._get_temperature(temp_data), self._get_humidity(humidity_data)
    else:
        return 0, 0

def write(self, value):
    self.i2c.write(struct.pack(">H", value))

def close(self):
    """Closes the i2c connection"""
    self.i2c.close()

def __enter__(self):
    """used to enable python's with statement support"""
    return self

def __exit__(self, *exc_info):
    """with support"""
    self.close()

@staticmethod
def _calculate_checksum(value):
    """4.12 Checksum Calculation from an unsigned short input"""
    # CRC
    polynomial = 0x131  # //P(x)=x^8+x^5+x^4+1 = 100110001
    crc = 0xFF

    # calculates 8-Bit checksum with given polynomial
    for byteCtr in [ord(x) for x in struct.pack(">H", value)]:
        crc ^= byteCtr
        for bit in range(8, 0, -1):
            if crc & 0x80:
                crc = (crc << 1) ^ polynomial
            else:
                crc = (crc << 1)
    return crc

@staticmethod
def _get_temperature(unadjusted):
    """This function reads the first two bytes of data and        returns the temperature in C by using the following function:        T = 45 + (175 * (ST/2^16))        where ST is the value from the sensor        """
    unadjusted *= 175.0
    unadjusted /= 1 << 16  # divide by 2^16
    unadjusted -= 45
    return unadjusted

@staticmethod
def _get_humidity(unadjusted):
    """This function reads the first two bytes of data and returns        the relative humidity in percent by using the following function:        RH = (100 * (SRH / 2 ^16))        where SRH is the value read from the sensor        """
    unadjusted *= 100.0
    unadjusted /= 1 << 16  # divide by 2^16
    unadjusted -= 0
    return unadjusted

if name == "main":
try:
with SHT31(1) as sht31:
print sht31.check_heater_status()
sht31.turn_heater_on()
print sht31.check_heater_status()
sht31.turn_heater_off()
print sht31.check_heater_status()
temperature, humidity = sht31.get_temp_and_humidity()
print "Temperature: %s" % temperature
print "Humidity: %s" % humidity
except IOError, e:
print e
print "Error creating connection to i2c. This must be run as root"


Reply to this email directly or view it on GitHub
#6 (comment).

@jaques
Copy link
Owner

jaques commented Dec 31, 2015

Hi,

Glad to hear that everything is working. Just to confirm:

  1. Which version of the code did you use? The one @nadanks7 sent or the one I posted above (which was based on @nadanks7 's version)

  2. Is the negative temperature expected? Do you have the sensor outside?

@jimbowarrior
Copy link

it's the same code, both work very well.

Sensor is outside, I would know if negatif temperature are managed.

2015-12-31 8:01 GMT+01:00 jaques [email protected]:

Hi,

Glad to hear that everything is working. Just to confirm:

  1. Which version of the code did you use? The one @nadanks7
    https://github.com/nadanks7 sent or the one I posted above (which was
    based on @nadanks7 https://github.com/nadanks7 's version)

  2. Is the negative temperature expected? Do you have the sensor outside?


Reply to this email directly or view it on GitHub
#6 (comment).

@jimbowarrior
Copy link

Jaques, you propose this code to used your program

import sht21
with sht21.SHT21(0) as sht21:
print "Temperature: %s"%sht21.read_temperature()
print "Humidity: %s"%sht21.read_humidity()

I tried the same, with Nadanks7's code

import sht31
with sht31.SHT31(1) as sht31:
temperature, humidity = sht31.get_temp_and_humidity()
print "Temperature: %s" % temperature
print "Humidity: %s" % humidity

I get a error message
AttributeError: SHT31 instance has no attribute 'get_temp_and_humidity'

@jaques
Copy link
Owner

jaques commented Jan 2, 2016

Hello,

I tweaked @nadanks7's version of the code and renamed Measure_Temp_and_Humidity to get_temp_and_humidity. The only reason for doing it was to make the code a bit more pythonic (PEP8 says that methods should be lowercase).

Try either

  • changing get_temp_and_humidity to Measure_Temp_and_Humidity
  • Using the code I posted above for SHT31

Hope that helps,

Richard.

@jimbowarrior
Copy link

ok, very well ! I used your code more compliance :-)

@jaques
Copy link
Owner

jaques commented Jan 2, 2016

I've committed this version of the code to the repo. Thanks for testing.

@jimbowarrior
Copy link

May be I give you my final code. The aim will be monitored humidity and temperature to switch on both or only one output (Raspberry output) for supply 2 resistors and melt snow.

Cold > only one resistor on
Very cold > both resistors on

currently I have build this code with manual input :-)

import RPi.GPIO as gpio
import time
import netsnmp
import argparse
import os, sys

# on passe en mode BMC qui veut dire que nous allons utiliser directement
# le numero GPIO plutot que la position physique sur la carte
gpio.setmode(gpio.BCM)

# defini les port GPIO 4/26 comme etant des sortie output
gpio.setup(4, gpio.OUT)
gpio.setup(26, gpio.OUT)

#la je dois trouver correctement comment interroger la sonde par SNMP
#temperature = netsnmp.snmpwalk(oid, Version=2, DestHost='192.168.1.200', Community='pub')
#humidity = netsnmp.snmpwalk(oid, Version=2, DestHost='192.168.1.200', Community='pub')


#demande la saisie users
temperature = input("quelle temperature : ")
humidity = input("quelle taux humidity : ")


print temperature
print humidity

if 0 < temperature <= 5 and humidity >= 50:
   gpio.output(26, gpio.HIGH)
   gpio.output(4, gpio.LOW)

elif -6 < temperature <= 0 and humidity >= 50:
   gpio.output(26, gpio.HIGH)
   gpio.output(4, gpio.HIGH)
else :
   gpio.output(26, gpio.LOW)
   gpio.output(4, gpio.LOW)

works fine, checked with Led.
I can import sht31 and replace manual input.

Thanks you.

@nikinikiniki
Copy link

tested on raspberry Pi 2B with SHT30.Works well !
in "README"
import sht21 -->import sht31
with SHT31(1)-->with sht31.SHT31(1)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants