MicroPython Driver for SHT40 / SHT45
Contents
Introduction
This a MicroPython driver written specifically for the BBC micro:bit that will work with any of the SHT4x relative humidity and ambient temperature sensor series. The driver implements all functions described in the product datasheet.
The SHT4x sensor series is discussed in some detail here. The most common breakout boards use the SHT40 (or SHT41) and SHT45 sensors.
For future reference where the term SHT4x module is used it is referring to any SHT40, SHT41 or SHT45 breakout board. They all have the same pinout and sensor functionality.
The breakout boards are generally not labelled and with the very small sized sensor it's easy to mix the boards up. Often the only way to definitively determine the board's sensor is to examine it under a digital microscope.
Connecting the SHT4x module
The SHT4x communicates via I2C. The micro:bit and the SHT4x module must be connected as follows:
micro:bit | SHT4x Module |
---|---|
3.3V | VCC |
GND | GND |
Pin 19 | SCL |
Pin 20 | SDA |
This utilises the standard I2C pins of the micro:bit.
Driver Overview
The driver code can be:
- Copied from this webpage onto the clipboard then pasted into the MicroPython editor e.g. the Mu Editor. It should be saved as fc_sht4x.py - OR -
- Download as a zip file using the link. Unzip the file and save it as fc_sht4x.py into the default directory where the MicroPython editor e.g. Mu Editor saves python code files.
After saving the fc_sht4x.py file to the computer it should be copied to the small filesystem on the micro:bit. The examples on this page will not work if this step is omitted. Any MicroPython editor that recognises the micro:bit will have an option to do this.
The Mu Editor is recommended for its simplicity. It provides a Files button on the toolbar for just this purpose.
The driver is implemented as a class. The first thing to do is call the constructor. This provides an instance of the class.
from fc_sht4x import *
my_sensor = SHT4X()
The constructor has no arguments. It is expected that the SHT4x pins (SCL, SDA) will be wired to the default I2C pins of the micro:bit as described in the previous section.
The I2C slave address of the SHT4X is 0x44 and is fixed at the point of manufacture. There are no options on the chip to change this address. It is defined as a constant (ADDR) at the top of the driver code file.
This assumes that the file fc_sht4x.py has been successfully copied to the micro:bit's filesystem as described above.
The driver provides methods that:
- Reads the relative humidity and ambient temperature
- Operates the onboard heater
- Returns the chip's serial number
- Resets the sensor
Following is a full list of methods that are user callable. Each method is described and example use given in the sections below.
Read() | T() | T_f() | RH() |
Heater() | Serial() | Reset() |
Reading Temperature and Humidity
There are four methods that return the humidity and/or temperature.
- Read(precision = HIGH)
- T(precision = HIGH)
- T_f(precision = HIGH)
- RH(precision = HIGH)
Valid values for precision are HIGH, MEDIUM, LOW
If any of the four methods are called without an argument then the value(s) returned are of high precision.
Read(precision = HIGH)
Returns a tuple containing five values:
[0] is temperature in °C,
[1] is temperature in °F,
[2] is relative humidity in %,
[3] compares check sums for the
temperature and returns
True (good value) or False (bad value),
[4] compares check sums for the
relative humidity and returns
True (good value) or False (bad value)
T(precision = HIGH)
Returns temperature in °C
T_f(precision = HIGH)
Returns temperature in °F
RH(precision = HIGH)
Returns relative humidity in °%
Examples:
from fc_sht4x import *
sensor = SHT4X()
print(sensor.Read(LOW))
print('Temperature (C), High precision:', sensor.T())
print('Temperature (F), Medium precision:', sensor.T_f(MEDIUM))
print('Humidity (%), Low precision:', sensor.RH(LOW))
Sample Output:
(27.85992, 82.14786, 39.52338, True, True)
Temperature (C), High precision: 27.9
Temperature (F), Medium precision: 82.2
Humidity (%), Low precision: 40
Notes:
When the humidity is very high the sensor can return values greater than 100%. Likewise when the humidity is very low the sensor can return values less than 0%. The Read() method will return these non-physical values "uncropped" as it may be of use at these boundary conditions.
The RH() method "crops" these non-physical values to 100% and 0% respectively.
The values returned by the Read() method are at full floating point precision (five decimals) as received from the conversion calculations.
Temperatures from the T() and T_f() methods are rounded to one decimal to reflect the sensor accuracy.
Relative humidities from the RH() method is returned as an integer which again reflects the sensor accuracy.
Using the Onboard Heater
The sensor chip has an onboard heater. See the product datasheet (p13-14) for possible heater user cases.
The heater has three possible power settings (200mW, 110mW and 20mW) and two possible timer settings (0.1 second and 1 second). This gives a total of six different heating combinations.
The Heater(setting=5) method controls the heater.
Calling this method turns on the heater at the requested power level and for time interval. Just before the heater is deactivated a reading is taken and the method returns these values.
The valid values for the setting parameter are:
Setting | Power (mW) | Time (s) |
---|---|---|
0 | 200 | 1 |
1 | 200 | 0.1 |
2 | 110 | 1 |
3 | 110 | 0.1 |
4 | 20 | 1 |
5 | 20 | 0.1 |
Heater(setting=5)
(1) The values for setting are given in the above table.
(2) Returns a tuple containing five values:
[0] is temperature in °C,
[1] is temperature in °F,
[2] is relative humidity in %,
[3] compares check sums for the
temperature and returns
True (good value) or False (bad value),
[4] compares check sums for the
relative humidity and returns
True (good value) or False (bad value)
Example:
from microbit import sleep
from fc_sht4x import *
sensor = SHT4X()
# Get initial reading
print('Initial readings:')
print(sensor.Read())
print()
# Activate heater: 20mW for 1 second
print('Heater on')
print(sensor.Heater(4))
print()
# Wait 5 seconds then take reading
sleep(5000)
print('Reading after cooling down:')
print(sensor.Read())
Typical Output:
Initial readings:
(27.96139, 82.3305, 40.07652, True, True)
Heater on
(33.86549, 92.95789, 39.24681, True, True)
Reading after cooling down:
(28.0602, 82.50836, 39.06752, True, True)
Serial Number
A unique 32-bit serial number is assigned to each sensor chip at the time of manufacture. This serial number can be obtained by calling the Serial() method.
The product datasheet doesn't give guidance on how this serial number should be formatted. For the sake of human readability this method returns the serial number as a string consisting of two 16-bit numbers separated by a '-' character.
Example:
from fc_sht4x import *
sensor = SHT4X()
print(sensor.Serial())
Typical Output:
3679-42636
Resetting the Sensor
The user can send a soft reset command to the sensor chip. This can be done by calling the Reset() method. This is quick with the sensor taking less than 1mS to return to a full state of readiness.
from fc_sht4x import *
sensor = SHT4X()
sensor.Reset()
Enjoy!
SHT40, SHT45 Driver Code for micro:bit
Download as zip file
'''
SHT40, SHT41, SHT43, SHT45
MicroPython driver for micro:bit
AUTHOR: fredscave.com
DATE : 2024/10
VERSION : 1.00
'''
from microbit import i2c, sleep
from micropython import const
ADDR = const(0x44)
CMD_HI_PREC = const(0xFD)
TIME_HI_PREC = const(10)
CMD_MED_PREC = const(0xF6)
TIME_MED_PREC = const(5)
CMD_LO_PREC = const(0xE0)
TIME_LO_PREC = const(2)
HIGH = const(0)
MEDIUM = const(1)
LOW = const(2)
prec_cmd_list = [CMD_HI_PREC,
CMD_MED_PREC,
CMD_LO_PREC]
prec_wait_list = [TIME_HI_PREC,
TIME_MED_PREC,
TIME_LO_PREC]
CMD_HEAT0 = const(0x39) #200mw, 1s
CMD_HEAT1 = const(0x32) #200mw, 0.1s
CMD_HEAT2 = const(0x2F) #110mw, 1s
CMD_HEAT3 = const(0x24) #110mw, 0.1s
CMD_HEAT4 = const(0x1E) #20mw, 1s
CMD_HEAT5 = const(0x15) #20mw, 0.1s
heat_cmd_list = [CMD_HEAT0, CMD_HEAT1, CMD_HEAT2,
CMD_HEAT3, CMD_HEAT4, CMD_HEAT5]
heat_wait_list = [1000 + TIME_HI_PREC + 2,
100 + TIME_HI_PREC + 2,
1000 + TIME_HI_PREC + 2,
100 + TIME_HI_PREC + 2,
1000 + TIME_HI_PREC + 2,
100 + TIME_HI_PREC + 2]
CMD_SERIAL = const(0x89)
CMD_RESET = const(0x94)
CRC_INITIAL = const(0xFF)
CRC_POLY = const(0x31)
class SHT4X():
def Read(self, precision=HIGH):
if precision not in [HIGH, MEDIUM, LOW]:
precision = HIGH
cmd = prec_cmd_list[precision]
wait = prec_wait_list[precision]
i2c.write(ADDR, bytes([cmd]))
sleep(wait)
buf = i2c.read(ADDR, 6)
return SHT4X.convert(buf)
def T(self, precision=HIGH):
results = self.Read(precision)
return round(results[0], 1)
def T_f(self, precision=HIGH):
results = self.Read(precision)
return round(results[1], 1)
def RH(self, precision=HIGH):
results = self.Read(precision)
rh = results[2]
if rh > 100:
rh = 100
elif rh < 0:
rh = 0
return int(rh + 0.5)
def Heater(self, setting=5):
if setting not in range(6):
setting = 5
cmd = heat_cmd_list[setting]
wait = heat_wait_list[setting]
i2c.write(ADDR, bytes([cmd]))
sleep(wait)
buf = i2c.read(ADDR, 6)
return SHT4X.convert(buf)
def Serial(self):
i2c.write(ADDR, bytes([CMD_SERIAL]))
sleep(5)
buf = i2c.read(ADDR, 6)
upper = buf[0] * 256 + buf[1]
lower = buf[3] * 256 + buf[4]
return str(upper) + '-' + str(lower)
def Reset(self):
i2c.write(ADDR, bytes([CMD_RESET]))
sleep(1)
@staticmethod
def checksum(buf):
crc = CRC_INITIAL
for byte in buf:
crc ^= byte
for bit in range(8):
if crc & 0x80:
crc = (crc << 1) ^ CRC_POLY
else:
crc = crc << 1
return crc & 0xFF
@staticmethod
def convert(buf):
t_ticks = buf[0] * 256 + buf[1]
t_c = -45 + 175 * t_ticks/65535
t_f = -49 + 315 * t_ticks/65535
t_buf = bytearray([buf[0], buf[1]])
t_check = SHT4X.checksum(t_buf)
rh_ticks = buf[3] * 256 + buf[4]
rh_p = -6 + 125 * rh_ticks/65535
rh_buf = bytearray([buf[3], buf[4]])
rh_check = SHT4X.checksum(rh_buf)
return (t_c,
t_f,
rh_p,
t_check == buf[2],
rh_check == buf[5])