]> vault307.fbx.one Git - SensorLib.git/blob - si7021.py
sensors
[SensorLib.git] / si7021.py
1 '''This module implements a driver for the Si7021 humidity and temperature
2 sensor.
3
4 Datasheet:
5 https://www.silabs.com/Support%20Documents%2FTechnicalDocs%2FSi7021-A20.pdf
6
7 '''
8
9 from time import sleep
10
11 class CRCError(Exception):
12 'Data failed a CRC check.'
13 pass
14
15
16 class Si7021(object):
17 'Driver for the Si7021 temperature sensor.'
18 SI7021_DEFAULT_ADDRESS = 0x40
19 SI7021_MEASTEMP_NOHOLD_CMD = bytearray([0xF3])
20 SI7021_MEASRH_NOHOLD_CMD = bytearray([0xF5])
21 SI7021_RESET_CMD = bytearray([0xFE])
22 SI7021_ID1_CMD = bytearray([0xFA, 0x0F])
23 SI7021_ID2_CMD = bytearray([0xFC, 0xC9])
24 I2C_WAIT_TIME = 0.025
25
26
27 def __init__(self, i2c, address=SI7021_DEFAULT_ADDRESS):
28 'Initialize an Si7021 sensor object.'
29 self.i2c = i2c
30 self.address = address
31 self.serial, self.identifier = self._get_device_info()
32
33
34 @property
35 def temperature(self):
36 'Return the temperature in Celcius.'
37 temperature = self._get_data(self.SI7021_MEASTEMP_NOHOLD_CMD)
38 celcius = temperature * 175.72 / 65536 - 46.85
39 return celcius
40
41
42 @temperature.setter
43 def temperature(self, value):
44 raise AttributeError('can\'t set attribute')
45
46
47 @property
48 def relative_humidity(self):
49 'Return the relative humidity as a percentage. i.e. 35.59927'
50 relative_humidity = self._get_data(self.SI7021_MEASRH_NOHOLD_CMD)
51 relative_humidity = relative_humidity * 125 / 65536 - 6
52 return relative_humidity
53
54
55 @relative_humidity.setter
56 def relative_humidity(self, value):
57 raise AttributeError('can\'t set attribute')
58
59
60 def reset(self):
61 'Reset the sensor.'
62 self.i2c.writeto(self.address, self.SI7021_RESET_CMD)
63 sleep(self.I2C_WAIT_TIME)
64
65
66 def _get_data(self, command):
67 'Retrieve data from the sensor and verify it with a CRC check.'
68 data = bytearray(3)
69 self.i2c.writeto(self.address, command)
70 sleep(self.I2C_WAIT_TIME)
71
72 self.i2c.readfrom_into(self.address, data)
73 value = self._convert_to_integer(data[:2])
74
75 verified = self._verify_checksum(data)
76 if not verified:
77 raise CRCError('Data read off i2c bus failed CRC check.',
78 data[:2],
79 data[-1])
80 return value
81
82
83 def _get_device_info(self):
84 '''Get the serial number and the sensor identifier. The identifier is
85 part of the bytes returned for the serial number.
86
87 '''
88 # Serial 1st half
89 self.i2c.writeto(self.address, self.SI7021_ID1_CMD)
90 id1 = bytearray(8)
91 sleep(self.I2C_WAIT_TIME)
92 self.i2c.readfrom_into(self.address, id1)
93
94 # Serial 2nd half
95 self.i2c.writeto(self.address, self.SI7021_ID2_CMD)
96 id2 = bytearray(6)
97 sleep(self.I2C_WAIT_TIME)
98 self.i2c.readfrom_into(self.address, id2)
99
100 combined_id = bytearray([id1[0], id1[2], id1[4], id1[6],
101 id2[0], id2[1], id2[3], id2[4]])
102
103 serial = self._convert_to_integer(combined_id)
104 identifier = self._get_device_identifier(id2[0])
105
106 return serial, identifier
107
108 def _convert_to_integer(self, bytes_to_convert):
109 'Use bitwise operators to convert the bytes into integers.'
110 integer = None
111 for chunk in bytes_to_convert:
112 if not integer:
113 integer = chunk
114 else:
115 integer = integer << 8
116 integer = integer | chunk
117 return integer
118
119
120 def _get_device_identifier(self, identifier_byte):
121 '''Convert the identifier byte to a device identifier. Values are based
122 on the information from page 24 of the datasheet.
123
124 '''
125 if identifier_byte == 0x00 or identifier_byte == 0xFF:
126 return 'engineering sample'
127 elif identifier_byte == 0x0D:
128 return 'Si7013'
129 elif identifier_byte == 0x14:
130 return 'Si7020'
131 elif identifier_byte == 0x15:
132 return 'Si7021'
133 else:
134 return 'unknown'
135
136
137 def _verify_checksum(self, data):
138 ''''Verify the checksum using the polynomial from page 19 of the
139 datasheet.
140
141 x8 + x5 + x4 + 1 = 0x131 = 0b100110001
142
143 Valid Example:
144 byte1: 0x67 [01100111]
145 byte2: 0x8c [10001100]
146 byte3: 0xfc [11111100] (CRC byte)
147
148 '''
149 crc = 0
150 values = data[:2]
151 checksum = int(data[-1])
152 for value in values:
153 crc = crc ^ value
154 for _ in range(8, 0, -1):
155 if crc & 0x80: #10000000
156 crc <<= 1
157 crc ^= 0x131 #100110001
158 else:
159 crc <<= 1
160 if crc != checksum:
161 return False
162 else:
163 return True
164
165 def convert_celcius_to_fahrenheit(celcius):
166 'Convert a Celcius measurement into a Fahrenheit measurement.'
167 return celcius * 1.8 + 32