aboutsummaryrefslogtreecommitdiffstats
path: root/klippy
diff options
context:
space:
mode:
Diffstat (limited to 'klippy')
-rw-r--r--klippy/extras/heaters.py1
-rw-r--r--klippy/extras/htu21d.py238
2 files changed, 239 insertions, 0 deletions
diff --git a/klippy/extras/heaters.py b/klippy/extras/heaters.py
index 29fb5d6e..cdb5c186 100644
--- a/klippy/extras/heaters.py
+++ b/klippy/extras/heaters.py
@@ -265,6 +265,7 @@ class PrinterHeaters:
self.printer.try_load_module(config, "adc_temperature")
self.printer.try_load_module(config, "spi_temperature")
self.printer.try_load_module(config, "bme280")
+ self.printer.try_load_module(config, "htu21d")
sensor_type = config.get('sensor_type')
if sensor_type not in self.sensor_factories:
raise self.printer.config_error(
diff --git a/klippy/extras/htu21d.py b/klippy/extras/htu21d.py
new file mode 100644
index 00000000..3c9e0590
--- /dev/null
+++ b/klippy/extras/htu21d.py
@@ -0,0 +1,238 @@
+# HTU21D(F)/Si7013/Si7020/Si7021/SHT21 i2c based temperature sensors support
+#
+# Copyright (C) 2020 Lucio Tarantino <lucio.tarantino@gmail.com>
+#
+# This file may be distributed under the terms of the GNU GPLv3 license.
+
+######################################################################
+# NOTE: The implementation requires write support of length 0
+# before reading on the i2c bus of the mcu.
+#
+# Compatible Sensors:
+# HTU21D - Tested on Linux MCU.
+# Si7013 - Untested
+# Si7020 - Untested
+# Si7021 - Untested
+# SHT21 - Untested
+#
+######################################################################
+
+import bus
+import logging
+
+HTU21D_I2C_ADDR= 0x40
+
+HTU21D_COMMANDS = {
+ 'HTU21D_TEMP' :0xE3,
+ 'HTU21D_HUMID' :0xE5,
+ 'HTU21D_TEMP_NH' :0xF3,
+ 'HTU21D_HUMID_NH' :0xF5,
+ 'WRITE' :0xE6,
+ 'READ' :0xE7,
+ 'RESET' :0xFE,
+ 'SERIAL' :[0xFA,0x0F,0xFC,0xC9],
+ 'FIRMWARE_READ' :[0x84,0xB8]
+
+}
+
+HTU21D_RESOLUTION_MASK = 0x7E;
+HTU21D_RESOLUTIONS = {
+ 'TEMP14_HUM12':int('00000000',2),
+ 'TEMP13_HUM10':int('10000000',2),
+ 'TEMP12_HUM08':int('00000001',2),
+ 'TEMP11_HUM11':int('10000001',2)
+}
+
+# Device with conversion time for tmp/resolution bit
+# The format is:
+# <CHIPNAME>:{id:<ID>, ..<RESOlUTION>:[<temp time>,<humidity time>].. }
+HTU21D_DEVICES = {
+ 'SI7013':{'id':0x0D,
+ 'TEMP14_HUM12':[.11,.12],
+ 'TEMP13_HUM10':[ .7, .5],
+ 'TEMP12_HUM08':[ .4, .4],
+ 'TEMP11_HUM11':[ .3, .7]},
+ 'SI7020':{'id':0x14,
+ 'TEMP14_HUM12':[.11,.12],
+ 'TEMP13_HUM10':[ .7, .5],
+ 'TEMP12_HUM08':[ .4, .4],
+ 'TEMP11_HUM11':[ .3, .7]},
+ 'SI7021':{'id':0x14,
+ 'TEMP14_HUM12':[.11,.12],
+ 'TEMP13_HUM10':[ .7, .5],
+ 'TEMP12_HUM08':[ .4, .4],
+ 'TEMP11_HUM11':[ .3, .7]},
+ 'SHT21': {'id':0x31,
+ 'TEMP14_HUM12':[.85,.29],
+ 'TEMP13_HUM10':[.43, .9],
+ 'TEMP12_HUM08':[.22, .4],
+ 'TEMP11_HUM11':[.11,.15]},
+ 'HTU21D':{'id':0x32,
+ 'TEMP14_HUM12':[.50,.16],
+ 'TEMP13_HUM10':[.25, .5],
+ 'TEMP12_HUM08':[.13, .3],
+ 'TEMP11_HUM11':[.12, .8]}
+}
+#temperature coefficient for RH compensation at range 0C..80C,
+# for HTU21D & SHT21 only
+HTU21D_TEMP_COEFFICIENT= -0.15
+#crc8 polynomial for 16bit value, CRC8 -> x^8 + x^5 + x^4 + 1
+HTU21D_CRC8_POLYNOMINAL= 0x13100
+
+class HTU21D:
+ 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=HTU21D_I2C_ADDR, default_speed=100000)
+ self.hold_master_mode = config.getboolean('htu21d_hold_master',False)
+ self.resolution = config.get('htu21d_resolution','TEMP12_HUM08')
+ self.report_time = config.getint('htu21d_report_time',30,minval=5)
+ if self.resolution not in HTU21D_RESOLUTIONS:
+ raise config.error("Invalid HTU21D Resolution. Valid are %s"
+ % '|'.join(HTU21D_RESOLUTIONS.keys()))
+ self.deviceId = config.get('sensor_type')
+ self.temp = self.humidity = 0.
+ self.sample_timer = self.reactor.register_timer(self._sample_htu21d)
+ self.printer.add_object("htu21d " + self.name, self)
+ self.printer.register_event_handler("klippy:ready", self.handle_ready)
+
+ def handle_ready(self):
+ self._init_htu21d()
+ self.reactor.update_timer(self.sample_timer, self.reactor.NOW)
+
+ def setup_minmax(self, min_temp, max_temp):
+ pass
+
+ def setup_callback(self, cb):
+ self._callback = cb
+
+ def _init_htu21d(self):
+ # Device Soft Reset
+ self.i2c.i2c_write([HTU21D_COMMANDS['RESET']])
+ # Wait 15ms after reset
+ self.reactor.pause(self.reactor.monotonic() + .15)
+
+ # Read ChipId
+ params = self.i2c.i2c_read([HTU21D_COMMANDS['SERIAL'][2],
+ HTU21D_COMMANDS['SERIAL'][3]], 3)
+ response = bytearray(params['response'])
+ rdevId = response[0] << 8
+ rdevId |= response[1]
+ checksum = response[2]
+ if self._chekCRC8(rdevId) != checksum:
+ logging.warn("htu21d: Reading deviceId !Checksum error!")
+ rdevId = rdevId >> 8
+ deviceId_list = list(
+ filter(
+ lambda elem: HTU21D_DEVICES[elem]['id'] == rdevId,HTU21D_DEVICES)
+ )
+ if len(deviceId_list) != 0:
+ logging.info("htu21d: Found Device Type %s" % deviceId_list[0])
+ else:
+ logging.warn("htu21d: Unknown Device ID %#x " % rdevId)
+
+ if(self.deviceId != deviceId_list[0]):
+ logging.warn(
+ "htu21d: Found device %s. Forcing to type %s as config.",
+ deviceId_list[0],self.deviceId)
+
+ # Set Resolution
+ params = self.i2c.i2c_read([HTU21D_COMMANDS['READ']], 1)
+ response = bytearray(params['response'])
+ registerData = response[0] & HTU21D_RESOLUTION_MASK
+ registerData |= HTU21D_RESOLUTIONS[self.resolution]
+ self.i2c.i2c_write([HTU21D_COMMANDS['WRITE']],registerData)
+ logging.info("htu21d: Setting resolution to %s " % self.resolution)
+
+ def _sample_htu21d(self, eventtime):
+ try:
+ # Read Temeprature
+ if self.hold_master_mode:
+ params = self.i2c.i2c_write([HTU21D_COMMANDS['HTU21D_TEMP']])
+ else:
+ params = self.i2c.i2c_write([HTU21D_COMMANDS['HTU21D_TEMP_NH']])
+
+ # Wait
+ self.reactor.pause(self.reactor.monotonic()
+ + HTU21D_DEVICES[self.deviceId][self.resolution][0])
+
+
+ params = self.i2c.i2c_read([],3)
+
+ response = bytearray(params['response'])
+ rtemp = response[0] << 8
+ rtemp |= response[1]
+ if self._chekCRC8(rtemp) != response[2]:
+ logging.warn("htu21d: Checksum error on Temperature reading!")
+ else:
+ self.temp = (0.002681 * float(rtemp) - 46.85)
+ logging.debug("htu21d: Temperature %.2f " % self.temp)
+
+ # Read Humidity
+ if self.hold_master_mode:
+ self.i2c.i2c_write([HTU21D_COMMANDS['HTU21D_HUMID']])
+ else:
+ self.i2c.i2c_write([HTU21D_COMMANDS['HTU21D_HUMID_NH']])
+
+ # Wait
+ self.reactor.pause(self.reactor.monotonic()
+ + HTU21D_DEVICES[self.deviceId][self.resolution][1])
+
+ params = self.i2c.i2c_read([],3)
+
+ response = bytearray(params['response'])
+ rhumid = response[0] << 8
+ rhumid|= response[1]
+ if self._chekCRC8(rhumid) != response[2]:
+ logging.warn("htu21d: Checksum error on Humidity reading!")
+ else:
+ #clear status bits,
+ # humidity always returns xxxxxx10 in the LSB field
+ rhumid ^= 0x02;
+ self.humidity = (0.001907 * float(rhumid) - 6)
+ if (self.humidity < 0):
+ #due to RH accuracy, measured value might be
+ # slightly less than 0 or more 100
+ self.temp = 0
+ elif (self.humidity > 100):
+ self.humidity = 100
+ # Only for HTU21D & SHT21.
+ # Calculates temperature compensated Humidity, %RH
+ if( self.deviceId in ['SHT21','HTU21D']
+ and self.temp > 0 and self.temp < 80):
+ logging.debug("htu21d: Do temp compensation..")
+ self.humidity = self.humidity
+ + (25.0 - self.temp) * HTU21D_TEMP_COEFFICIENT;
+ logging.debug("htu21d: Humidity %.2f " % self.humidity)
+ except Exception:
+ logging.exception("htu21d: Error reading data")
+ self.temp = self.humidity = .0
+ return self.reactor.NEVER
+
+ measured_time = self.reactor.monotonic()
+ self._callback(measured_time, self.temp)
+ return measured_time + self.report_time
+
+ def _chekCRC8(self,data):
+ for bit in range(0,16):
+ if (data & 0x8000):
+ data = (data << 1) ^ HTU21D_CRC8_POLYNOMINAL;
+ else:
+ data <<= 1
+ data = data >> 8
+ return data
+
+ def get_status(self, eventtime):
+ return {
+ 'temperature': self.temp,
+ 'humidity': self.humidity,
+ }
+
+
+def load_config(config):
+ # Register sensor
+ pheater = config.get_printer().lookup_object("heaters")
+ for stype in HTU21D_DEVICES:
+ pheater.add_sensor_factory(stype, HTU21D)