SHTC3 support #492
174
octoprint_enclosure/SHTC3.py
Normal file
174
octoprint_enclosure/SHTC3.py
Normal 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()
|
||||
@@ -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 "
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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">
|
||||
|
||||
2
setup.py
2
setup.py
@@ -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 = {}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user