In this example we connect a Si7021 I2C Humidity and Temperature Sensor to an ESP32 running Micropython
The Si7021 I2C Humidity and Temperature Sensor is a monolithic CMOS IC integrating humidity and temperature sensor elements, an analog-to-digital converter, signal processing, calibration data, and an I2C Interface. The patented
use of industry-standard, low-K polymeric dielectrics for sensing humidity enables the construction of low-power, monolithic CMOS Sensor ICs with low drift and hysteresis, and excellent long term stability.
The humidity and temperature sensors are factory-calibrated and the calibration data is stored in the on-chip non-volatile memory. This ensures that the sensors are fully interchangeable, with no recalibration or software changes required.
Parts List
I actually used a GY-21P which is a combined BMP280 and Si7021 sensor in one module. We only deal with the Si7021 module here
Connection
An easy module to connect to an ESP32, SCL is 22 and SDA is 21 on the Wemos board I used, you can see this is the schematic below
Code
You can use any method to upload files or an IDE for development.
The following is based on a github library – . The first part of this is the library which I upload to my ESP32
This is the library called si7021.py
'''This module implements a driver for the Si7021 humidity and temperature
sensor.
Datasheet:
Click to access Support%20Documents%2FTechnicalDocs%2FSi7021-A20.pdf
'''
from time import sleep
class CRCError(Exception):
'Data failed a CRC check.'
pass
class Si7021(object):
'Driver for the Si7021 temperature sensor.'
SI7021_DEFAULT_ADDRESS = 0x40
SI7021_MEASTEMP_NOHOLD_CMD = bytearray([0xF3])
SI7021_MEASRH_NOHOLD_CMD = bytearray([0xF5])
SI7021_RESET_CMD = bytearray([0xFE])
SI7021_ID1_CMD = bytearray([0xFA, 0x0F])
SI7021_ID2_CMD = bytearray([0xFC, 0xC9])
I2C_WAIT_TIME = 0.025
def __init__(self, i2c, address=SI7021_DEFAULT_ADDRESS):
'Initialize an Si7021 sensor object.'
self.i2c = i2c
self.address = address
self.serial, self.identifier = self._get_device_info()
@property
def temperature(self):
'Return the temperature in Celcius.'
temperature = self._get_data(self.SI7021_MEASTEMP_NOHOLD_CMD)
celcius = temperature * 175.72 / 65536 - 46.85
return celcius
@temperature.setter
def temperature(self, value):
raise AttributeError('can\'t set attribute')
@property
def relative_humidity(self):
'Return the relative humidity as a percentage. i.e. 35.59927'
relative_humidity = self._get_data(self.SI7021_MEASRH_NOHOLD_CMD)
relative_humidity = relative_humidity * 125 / 65536 - 6
return relative_humidity
@relative_humidity.setter
def relative_humidity(self, value):
raise AttributeError('can\'t set attribute')
def reset(self):
'Reset the sensor.'
self.i2c.writeto(self.address, self.SI7021_RESET_CMD)
sleep(self.I2C_WAIT_TIME)
def _get_data(self, command):
'Retrieve data from the sensor and verify it with a CRC check.'
data = bytearray(3)
self.i2c.writeto(self.address, command)
sleep(self.I2C_WAIT_TIME)
self.i2c.readfrom_into(self.address, data)
value = self._convert_to_integer(data[:2])
verified = self._verify_checksum(data)
if not verified:
raise CRCError('Data read off i2c bus failed CRC check.',
data[:2],
data[-1])
return value
def _get_device_info(self):
'''Get the serial number and the sensor identifier. The identifier is
part of the bytes returned for the serial number.
'''
# Serial 1st half
self.i2c.writeto(self.address, self.SI7021_ID1_CMD)
id1 = bytearray(8)
sleep(self.I2C_WAIT_TIME)
self.i2c.readfrom_into(self.address, id1)
# Serial 2nd half
self.i2c.writeto(self.address, self.SI7021_ID2_CMD)
id2 = bytearray(6)
sleep(self.I2C_WAIT_TIME)
self.i2c.readfrom_into(self.address, id2)
combined_id = bytearray([id1[0], id1[2], id1[4], id1[6],
id2[0], id2[1], id2[3], id2[4]])
serial = self._convert_to_integer(combined_id)
identifier = self._get_device_identifier(id2[0])
return serial, identifier
def _convert_to_integer(self, bytes_to_convert):
'Use bitwise operators to convert the bytes into integers.'
integer = None
for chunk in bytes_to_convert:
if not integer:
integer = chunk
else:
integer = integer << 8
integer = integer | chunk
return integer
def _get_device_identifier(self, identifier_byte):
'''Convert the identifier byte to a device identifier. Values are based
on the information from page 24 of the datasheet.
'''
if identifier_byte == 0x00 or identifier_byte == 0xFF:
return 'engineering sample'
elif identifier_byte == 0x0D:
return 'Si7013'
elif identifier_byte == 0x14:
return 'Si7020'
elif identifier_byte == 0x15:
return 'Si7021'
else:
return 'unknown'
def _verify_checksum(self, data):
''''Verify the checksum using the polynomial from page 19 of the
datasheet.
x8 + x5 + x4 + 1 = 0x131 = 0b100110001
Valid Example:
byte1: 0x67 [01100111]
byte2: 0x8c [10001100]
byte3: 0xfc [11111100] (CRC byte)
'''
crc = 0
values = data[:2]
checksum = int(data[-1])
for value in values:
crc = crc ^ value
for _ in range(8, 0, -1):
if crc & 0x80: #10000000
crc <<= 1
crc ^= 0x131 #100110001
else:
crc <<= 1
if crc != checksum:
return False
else:
return True
def convert_celcius_to_fahrenheit(celcius):
'Convert a Celcius measurement into a Fahrenheit measurement.'
return celcius * 1.8 + 32
Now you upload this library and then you can create or modify the main.py with this example code
import si7021
import machine
i2c = machine.I2C(sda=machine.Pin(21),scl=machine.Pin(22))
temp_sensor = si7021.Si7021(i2c)
print('Serial: {value}'.format(value=temp_sensor.serial))
print('Identifier: {value}'.format(value=temp_sensor.identifier))
print('Temperature: {value}'.format(value=temp_sensor.temperature))
print('Relative Humidity: {value}'.format(value=temp_sensor.relative_humidity))
Testing
Open up the REPL window. Here is what I saw in uPyCraft
Ready to download this file,please wait!
….
download ok
exec(open(‘./main.py’).read(),globals())
Serial: 9108027469264322559
Identifier: Si7021
Temperature: 23.03474
Relative Humidity: 36.35458
>>>
Links
https://www.silabs.com/documents/public/data-sheets/Si7021-A20.pdf
1 comment
Looks good, D
did you try 2 and more sensors on one esp32 ?