Added support for SHTC3 module

This commit is contained in:
Keir Rice
2022-09-24 21:14:15 +12:00
parent 6e35aa5940
commit f84374ca56
3 changed files with 209 additions and 2 deletions

View File

@@ -0,0 +1,185 @@
import sys
import time
from smbus2 import SMBus, i2c_msg
try:
import struct
except ImportError:
import ustruct as struct
from crc import CrcCalculator, Configuration
width = 8
poly = 0x31
init_value = 0xFF
final_xor_value = 0x00
reverse_input = False
reverse_output = False
configuration = Configuration(width, poly, init_value, final_xor_value, reverse_input, reverse_output)
use_table = True
crc_calculator = CrcCalculator(configuration, use_table)
class SHTC3Exception(Exception):
""" Base class for exception """
class SHTC3DeviceNotFound(SHTC3Exception, ValueError):
""" Device not found """
class SHTC3ReadError(SHTC3Exception, RuntimeError):
""" Read error or CRC mismatch """
CMD_SLEEP = 0xB098
CMD_WAKE = 0x3517 # 240µs
CMD_RESET = 0x805D # 240µs
CMD_READ_ID = 0xEFC8
# Normal sample time 12ms
# Low sample time 0.8ms
CMD_NORMAL_T = 0x7866
CMD_NORMAL_RH = 0x58E0
CMD_LOW_T = 0x609C
CMD_LOW_RH = 0x401A
CMD_NORMAL_STRETCH_T = 0x7CA2
CMD_NORMAL_STRETCH_RH = 0x5C24
CMD_LOW_STRETCH_T = 0x6458
CMD_LOW_STRETCH_RH = 0x44DE
SHTC3_I2CADDR_DEFAULT = 0x70
def send_command(bus, address, cmd):
"""Write the command bytes to the bus."""
byte_array = cmd.to_bytes(2, 'big')
for b in byte_array:
bus.write_byte_data(address, 0, b)
time.sleep(0.001)
def write_then_read(bus, address, cmd, read_length):
"""In a single transaction write a cmd, then read the result."""
cmd_bytes = cmd.to_bytes(2, 'big')
write = i2c_msg.write(address, cmd_bytes)
read = i2c_msg.read(address, read_length)
bus.i2c_rdwr(write, read)
return bytearray(read)
def process_crc(bytes_array):
"""Assume the last byte in the array is the CRC and check it matches the rest of the message.
Return just the message bytes."""
crc_byte = bytes_array[-1]
bytes_array = bytes_array[:-1]
if not crc_calculator.verify_checksum(bytes_array, crc_byte):
raise RuntimeError('Checksum failed')
return bytes_array
def to_relative_humidity(raw_data):
"""Convert the linearized 16 bit values into Relative humidity (result in %RH)
Source: https://sensirion.com/media/documents/643F9C8E/6164081E/Sensirion_Humidity_Sensors_SHTC3_Datasheet.pdf
"""
return 100.0 * (raw_data / 2 ** 16)
def to_temperature(raw_data):
"""Convert the linearized 16 bit values into Temperature (result in °C)
Source: https://sensirion.com/media/documents/643F9C8E/6164081E/Sensirion_Humidity_Sensors_SHTC3_Datasheet.pdf
"""
return -45 + 175 * (raw_data / 2 ** 16)
def sample_temperature(bus, address):
send_command(bus, CMD_RESET)
time.sleep(0.001)
send_command(bus, CMD_WAKE)
time.sleep(0.001)
try:
result = write_then_read(bus, address, CMD_NORMAL_STRETCH_T, 3)
raw_temp, crc = struct.unpack(">HI", result)
if not crc_calculator.verify_checksum(result[:-1], crc):
raise SHTC3ReadError('CRC Mismatch')
return to_temperature(raw_temp)
finally:
send_command(bus, CMD_SLEEP)
def sample_humidity(bus, address):
send_command(bus, address, CMD_RESET)
time.sleep(0.001)
send_command(bus, address, CMD_WAKE)
time.sleep(0.001)
try:
result = write_then_read(bus, address, CMD_NORMAL_STRETCH_RH, 3)
raw_rh, crc = struct.unpack(">HI", result)
if not crc_calculator.verify_checksum(result[:-1], crc):
raise SHTC3ReadError('CRC Mismatch')
return to_relative_humidity(raw_rh)
finally:
send_command(bus, address, CMD_SLEEP)
def sample_both(bus, address):
"""Sample of temperature and humidity."""
send_command(bus, address, CMD_RESET)
send_command(bus, address, CMD_WAKE)
try:
result = write_then_read(bus, address, CMD_NORMAL_STRETCH_T, 6)
raw_temp, crc_temp, raw_rh, crc_rh = struct.unpack(">HI>HI", result)
if not crc_calculator.verify_checksum(result[0:2], crc_temp):
raise SHTC3ReadError('CRC Mismatch')
if not crc_calculator.verify_checksum(result[3:5], raw_rh):
raise SHTC3ReadError('CRC Mismatch')
temperature = to_temperature(raw_temp)
humidity = to_relative_humidity(raw_rh)
return temperature, humidity
finally:
# Sleep
send_command(bus, address, CMD_SLEEP)
def main():
# get i2c bus and bus address if provided or use defaults
address = SHTC3_I2CADDR_DEFAULT
bus = SMBus(1)
if len(sys.argv) > 1:
bus = SMBus(int(sys.argv[1]))
address = int(sys.argv[2], 16)
try:
temperature, humidity = sample_both(bus, address)
print('{0:0.1f} | {1:0.1f}'.format(temperature, humidity))
except Exception:
print('-1 | -1')
if __name__ == "__main__":
main()

View File

@@ -1045,6 +1045,9 @@ class EnclosurePlugin(octoprint.plugin.StartupPlugin, octoprint.plugin.TemplateP
elif sensor['temp_sensor_type'] == "hum_raw_i2c":
hum, temp = self.read_raw_i2c_temp(sensor)
airquality = 0
elif sensor['temp_sensor_type'] == "shtc3":
temp, hum = self.read_shtc3_temp(sensor['temp_sensor_address'], sensor['temp_sensor_i2cbus'])
airquality = 0
else:
self._logger.info("temp_sensor_type no match")
temp = None
@@ -1137,6 +1140,24 @@ class EnclosurePlugin(octoprint.plugin.StartupPlugin, octoprint.plugin.TemplateP
self.log_error(ex)
return 0
def read_shtc3_temp(self, address, i2cbus):
try:
script = os.path.dirname(os.path.realpath(__file__)) + "/SHTC3.py"
args = ["python", script, str(i2cbus), str(address)]
if self._settings.get(["debug_temperature_log"]) is True:
self._logger.debug("Temperature SHTC3 cmd: %s", " ".join(args))
proc = Popen(args, stdout=PIPE)
stdout, _ = proc.communicate()
if self._settings.get(["debug_temperature_log"]) is True:
self._logger.debug("SHTC3 result: %s", stdout)
temp, hum = stdout.decode("utf-8").split("|")
return self.to_float(temp.strip()), self.to_float(hum.strip())
except Exception as ex:
self._logger.info("Failed to execute python scripts, try disabling use SUDO on advanced section.")
self.log_error(ex)
return 0, 0
def read_dht_temp(self, sensor, pin):
try:
script = os.path.dirname(os.path.realpath(__file__)) + "/getDHTTemp.py "

View File

@@ -617,6 +617,7 @@
<option value="max31855">MAX31855</option>
<option value="rpi">Raspberry Pi CPU</option>
<option value="mcp9808">MCP9808</option>
<option value="shtc3">SHTC3</option>
<option value="temp_raw_i2c">Raw I2C Temperature</option>
<option value="hum_raw_i2c">Raw I2C Humidity</option>
</select>
@@ -682,7 +683,7 @@
</div>
</div>
<!-- /ko -->
<!-- ko if: ($data.temp_sensor_type() == "si7021") || ($data.temp_sensor_type() == "20") || ($data.temp_sensor_type() == "bme280") || ($data.temp_sensor_type() == "am2320") || ($data.temp_sensor_type() == "aht10") || ($data.temp_sensor_type() == "tmp102") || ($data.temp_sensor_type() == "mcp9808") -->
<!-- ko if: ($data.temp_sensor_type() == "si7021") || ($data.temp_sensor_type() == "20") || ($data.temp_sensor_type() == "bme280") || ($data.temp_sensor_type() == "am2320") || ($data.temp_sensor_type() == "aht10") || ($data.temp_sensor_type() == "tmp102") || ($data.temp_sensor_type() == "mcp9808" || ($data.temp_sensor_type() == "shtc3") -->
<div class="control-group">
<label class="control-label" for="settings-enclosure-dhtPin">{{ _('Sensor Pin') }}</label>
<div class="controls">
@@ -732,7 +733,7 @@
</div>
<!-- /ko -->
<!-- ko ifnot: ($data.temp_sensor_type() == "rpi") || ($data.temp_sensor_type() == "18b20") || ($data.temp_sensor_type() == "si7021") || ($data.temp_sensor_type() == "bme280") || ($data.temp_sensor_type() == "am2320") || ($data.temp_sensor_type() == "tmp102") || ($data.temp_sensor_type() == "max31855") || ($data.temp_sensor_type() == "mcp9808") || ($data.temp_sensor_type() == "temp_raw_i2c") || ($data.temp_sensor_type() == "hum_raw_i2c") -->
<!-- ko ifnot: ($data.temp_sensor_type() == "rpi") || ($data.temp_sensor_type() == "18b20") || ($data.temp_sensor_type() == "si7021") || ($data.temp_sensor_type() == "bme280") || ($data.temp_sensor_type() == "am2320") || ($data.temp_sensor_type() == "tmp102") || ($data.temp_sensor_type() == "max31855") || ($data.temp_sensor_type() == "mcp9808") || ($data.temp_sensor_type() == "shtc3") || ($data.temp_sensor_type() == "temp_raw_i2c") || ($data.temp_sensor_type() == "hum_raw_i2c") -->
<div class="control-group">
<label class="control-label" for="settings-enclosure-dhtPin">{{ _('Sensor Pin') }}</label>
<div class="controls">