Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sensors: add sht31 support #6560

Merged
merged 1 commit into from
Apr 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions docs/Config_Reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -2574,6 +2574,25 @@ sensor_type:
# Interval in seconds between readings. Default is 30
```

### SHT3X sensor

SHT3X family two wire interface (I2C) environmental sensor. These sensors
have a range of -55~125 C, so are usable for e.g. chamber temperature
monitoring. They can also function as simple fan/heater controllers.

```
sensor_type: SHT3X
#i2c_address:
# Default is 68 (0x44).
#i2c_mcu:
#i2c_bus:
#i2c_software_scl_pin:
#i2c_software_sda_pin:
#i2c_speed:
# See the "common I2C settings" section for a description of the
# above parameters.
```

### LM75 temperature sensor

LM75/LM75A two wire (I2C) connected temperature sensors. These sensors
Expand Down
3 changes: 2 additions & 1 deletion docs/Status_Reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -445,14 +445,15 @@ The following information is available in

[bme280 config_section_name](Config_Reference.md#bmp280bme280bme680-temperature-sensor),
[htu21d config_section_name](Config_Reference.md#htu21d-sensor),
[sht3x config_section_name](Config_Reference.md#sht31-sensor),
[lm75 config_section_name](Config_Reference.md#lm75-temperature-sensor),
[temperature_host config_section_name](Config_Reference.md#host-temperature-sensor)
and
[temperature_combined config_section_name](Config_Reference.md#combined-temperature-sensor)
objects:
- `temperature`: The last read temperature from the sensor.
- `humidity`, `pressure`, `gas`: The last read values from the sensor
(only on bme280, htu21d, and lm75 sensors).
(only on bme280, htu21d, sht3x and lm75 sensors).

## temperature_fan

Expand Down
165 changes: 165 additions & 0 deletions klippy/extras/sht3x.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
# SHT3X i2c based temperature sensors support
#
# Copyright (C) 2024 Timofey Titovets <nefelim4ag@gmail.com>
# Based on htu21d.py code
#
# This file may be distributed under the terms of the GNU GPLv3 license.
import logging
from . import bus

######################################################################
# Compatible Sensors:
# SHT31 - Tested on octopus pro and Linux MCU
#
######################################################################
SHT3X_I2C_ADDR = 0x44
nefelim4ag marked this conversation as resolved.
Show resolved Hide resolved

SHT3X_CMD = {
'MEASURE': {
'STRETCH_ENABLED': {
'HIGH_REP': [0x2c, 0x06], # High (15ms) repeatability measurement
'MED_REP': [0x2c, 0x0D], # Medium (6ms) repeatability measurement
'LOW_REP': [0x2c, 0x10], # Low (4ms) repeatability measurement
},
'STRETCH_DISABLED' : {
'HIGH_REP': [0x24, 0x00],
'MED_REP': [0x24, 0x0B],
'LOW_REP': [0x24, 0x16],
},
},
'OTHER': {
'STATUS': {
'READ': [0xF3, 0x2D],
'CLEAN': [0x30, 0x41],
},
'SOFTRESET': [0x30, 0xA2], # Soft reset
'HEATER': {
"ENABLE": [0x30, 0x6D],
"DISABLE": [0x30, 0x66],
},
'FETCH': [0xE0, 0x00],
'BREAK': [0x30, 0x93],
}
}

class SHT3X:
def __init__(self, config):
self.printer = config.get_printer()
self.name = config.get_name().split()[-1]
self.reactor = self.printer.get_reactor()
self.i2c = bus.MCU_I2C_from_config(
config, default_addr=SHT3X_I2C_ADDR, default_speed=100000)
self.report_time = config.getint('sht3x_report_time', 1, minval=1)
self.deviceId = config.get('sensor_type')
self.temp = self.min_temp = self.max_temp = self.humidity = 0.
self.sample_timer = self.reactor.register_timer(self._sample_sht3x)
self.printer.add_object("sht3x " + self.name, self)
self.printer.register_event_handler("klippy:connect",
self.handle_connect)
def handle_connect(self):
self._init_sht3x()
self.reactor.update_timer(self.sample_timer, self.reactor.NOW)

def setup_minmax(self, min_temp, max_temp):
self.min_temp = min_temp
self.max_temp = max_temp

def setup_callback(self, cb):
self._callback = cb

def get_report_time_delta(self):
return self.report_time

def _init_sht3x(self):
# Device Soft Reset
self.i2c.i2c_write(SHT3X_CMD['OTHER']['SOFTRESET'])

# Wait 2ms after reset
self.reactor.pause(self.reactor.monotonic() + .02)

status = self.i2c.i2c_read(SHT3X_CMD['OTHER']['STATUS']['READ'], 3)
response = bytearray(status['response'])
status = response[0] << 8
status |= response[1]
checksum = response[2]

if self._crc8(status) != checksum:
logging.warning("sht3x: Reading status - checksum error!")

def _sample_sht3x(self, eventtime):
try:
# Read Temeprature
params = self.i2c.i2c_write(
SHT3X_CMD['MEASURE']['STRETCH_ENABLED']['HIGH_REP']
)
# Wait
self.reactor.pause(self.reactor.monotonic()
+ .20)

params = self.i2c.i2c_read([], 6)

response = bytearray(params['response'])
rtemp = response[0] << 8
rtemp |= response[1]
if self._crc8(rtemp) != response[2]:
logging.warning(
"sht3x: Checksum error on Temperature reading!"
)
else:
self.temp = -45 + (175 * rtemp / 65535)
logging.debug("sht3x: Temperature %.2f " % self.temp)

rhumid = response[3] << 8
rhumid |= response[4]
if self._crc8(rhumid) != response[5]:
logging.warning("sht3x: Checksum error on Humidity reading!")
else:
self.humidity = 100 * rhumid / 65535
logging.debug("sht3x: Humidity %.2f " % self.humidity)

except Exception:
logging.exception("sht3x: Error reading data")
self.temp = self.humidity = .0
return self.reactor.NEVER

if self.temp < self.min_temp or self.temp > self.max_temp:
self.printer.invoke_shutdown(
"sht3x: temperature %0.1f outside range of %0.1f:%.01f"
% (self.temp, self.min_temp, self.max_temp))

measured_time = self.reactor.monotonic()
print_time = self.i2c.get_mcu().estimated_print_time(measured_time)
self._callback(print_time, self.temp)
return measured_time + self.report_time

def _split_bytes(self, data):
bytes = []
for i in range((data.bit_length() + 7) // 8):
bytes.append((data >> i*8) & 0xFF)
bytes.reverse()
return bytes

def _crc8(self, data):
#crc8 polynomial for 16bit value, CRC8 -> x^8 + x^5 + x^4 + 1
SHT3X_CRC8_POLYNOMINAL= 0x31
crc = 0xFF
data_bytes = self._split_bytes(data)
for byte in data_bytes:
crc ^= byte
for _ in range(8):
if crc & 0x80:
crc = (crc << 1) ^ SHT3X_CRC8_POLYNOMINAL
else:
crc <<= 1
return crc & 0xFF

def get_status(self, eventtime):
return {
'temperature': round(self.temp, 2),
'humidity': round(self.humidity, 1),
}
nefelim4ag marked this conversation as resolved.
Show resolved Hide resolved

def load_config(config):
# Register sensor
pheater = config.get_printer().lookup_object("heaters")
pheater.add_sensor_factory("SHT3X", SHT3X)
2 changes: 2 additions & 0 deletions klippy/extras/temperature_sensors.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
# Load "SI7013", "SI7020", "SI7021", "SHT21", and "HTU21D" sensors
[htu21d]

[sht3x]

# Load "AHT10"
[aht10]

Expand Down
Loading