SHTC3 support #492

Open
KeirRice wants to merge 13 commits from KeirRice/master into master
5 changed files with 211 additions and 6 deletions

View File

@@ -0,0 +1,174 @@
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 bytes(read)
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, 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_T, 3)
if not crc_calculator.verify_checksum(result[0:2], result[2]):
raise SHTC3ReadError('CRC Mismatch')
raw_temp, crc = struct.unpack(">HB", result)
return to_temperature(raw_temp)
finally:
send_command(bus, address, 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)
if not crc_calculator.verify_checksum(result[0:2], result[2]):
raise SHTC3ReadError('CRC Mismatch')
raw_rh, crc = struct.unpack(">HB", result)
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)
if not crc_calculator.verify_checksum(result[0:2], result[2]):
raise SHTC3ReadError('CRC Mismatch')
if not crc_calculator.verify_checksum(result[3:5], result[5]):
raise SHTC3ReadError('CRC Mismatch')
raw_temp, crc_temp, raw_rh, crc_rh = struct.unpack(">HBHB", result)
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,33 @@ 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"
cmd = [sys.executable, script, str(i2cbus), str(address)]
if self._settings.get(["use_sudo"]):
cmd.insert(0, "sudo")
if self._settings.get(["debug_temperature_log"]) is True:
self._logger.debug("Temperature SHTC3 cmd: %s", " ".join(cmd))
stdout = Popen(cmd, stdout=PIPE, stderr=PIPE, universal_newlines=True)
output, errors = stdout.communicate()
if self._settings.get(["debug_temperature_log"]) is True:
if len(errors) > 0:
self._logger.error("SHTC3 error: %s", errors)
else:
self._logger.info("SHTC3 result: %s", output)
temp, hum = output.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

@@ -56,7 +56,7 @@ $(function () {
self.notifications = ko.observableArray([]);
self.humidityCapableSensor = function(sensor){
if (['11', '20', '22', '2302', 'bme280', 'bme680', 'am2320', 'aht10' , 'si7021', 'hum_raw_i2c', 'temp_raw_i2c'].indexOf(sensor) >= 0){
if (['11', '20', '22', '2302', 'bme280', 'bme680', 'am2320', 'aht10' , 'si7021', 'hum_raw_i2c', 'temp_raw_i2c', 'shtc3'].indexOf(sensor) >= 0){
return true;
}
return false;

View File

@@ -604,7 +604,7 @@
<select data-bind="value: temp_sensor_type">
<option value="">Select Sensor</option>
<option value="11">DHT11</option>
<option value="20">DHT20</option>
<option value="20">DHT20</option>
<option value="22">DHT22</option>
<option value="2302">AM2302</option>
<option value="18b20">DS18B20</option>
@@ -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>
@@ -663,7 +664,7 @@
<span class="help-inline">GPIO pin for temperature sensor, recommended to use 4 as DS18B20 has default support to pin #4 (BCM)</span>
</div>
</div>
<div class"control-group">
<div class="control-group">
<label class="control-label" for="settings-enclosure-dhtPin">{{ _('Input Pull Resistor') }}</label>
<div class="controls">
<select data-bind="value: input_pull_resistor">
@@ -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">

View File

@@ -33,7 +33,7 @@ plugin_url = "https://github.com/vitormhenrique/OctoPrint-Enclosure"
plugin_license = "AGPLv3"
# Any additional requirements besides OctoPrint should be listed here
plugin_requires = ["RPi.GPIO>=0.6.5", "requests>=2.7", "smbus2>=0.3.0", "gpiozero==1.6.2", "RPi.bme280", "bme680"]
plugin_requires = ["RPi.GPIO>=0.6.5", "requests>=2.7", "smbus2>=0.3.0", "gpiozero==1.6.2", "RPi.bme280", "bme680", "crc"]
additional_setup_parameters = {}