Si7021 I2C Humidity and Temperature Sensor example in Micropython

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

Name Link
ESP32 WEMOS WiFi & Bluetooth Battery ESP32 development tool ESP32 battery esp8266 ESP WROOM 32 ESP32
Si7021 Atmospheric Humidity Temperature Sensor Breakout Barometric Pressure BMP280 SI7021 for Arduino GY-21P
Connecting cables Free shipping Dupont line 120pcs 20cm male to male + male to female and female to female jumper wire

 

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:
https://www.silabs.com/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

LEAVE A REPLY

Please enter your comment!
Please enter your name here